/*! \file
 * \brief Enter a brief description here.
 *
 * Enter a detailed description here. (optional)
 * \author Author (Ivo Wingelaar)
 * \date Date (2011)
 * \version Version (0.0.1)
 * \copyright Copyright (GNU LGPL v3)
 */

#include "LynxVideoManager_C.hpp"
#include "LynxImage_C.hpp"
#include "LynxColorConverter.hpp"
#include "LynxMaterialRenderer_C.hpp"

#include "LynxNormalMapRenderer.hpp"
#include "LynxParallaxMapRenderer.hpp"
#include "LynxGLSLMaterialRenderer.hpp"
#include "LynxShaderMaterialRenderer.hpp"
#include <algorithm>
#include <stdio.h>

namespace Lynx
{

#ifdef _LYNX_WINDOWS_
//! constructor
VideoManager_C::VideoManager_C(LynxDeviceWindows_C* dev, const Myth::Math::Vector2u screenSize) :
    OpenGLExtensionHandler(dev->get_log_manager()),
    mDevice(dev),
    mTimeManager(dev->getTimeManager()),
    mLogManager(dev->get_log_manager()),
    mMeshManipulator(0),
    mViewPort(0,0,0,0),
    mScreenSize(screenSize),
    mPrimitivesDrawn(0),
    mMinVertexCountForVBO(500),
    mTextureCreationFlags(0),
    mOverrideMaterial2DEnabled(false),
    mAllowZWriteOnTransparent(false),
    mTransformation3DChanged(true),
    mCurrentRenderMode(ERM_NONE),
    mResetRenderStates(true),
    mAntiAlias(dev->mParameters.mAntiAlias),
    mRenderTargetTexture(0),
    mCurrentRendertargetSize(0,0),
    mColorFormat(ECF_R8G8B8),
    mCurrentTarget(ERT_FRAME_BUFFER)
{
    set_fog();

    set_texture_creation_flag(ETCF_ALWAYS_32_BIT, true);
    set_texture_creation_flag(ETCF_CREATE_MIP_MAPS, true);

    mViewPort = Myth::Math::Rectanglei(0,0, screenSize.X, screenSize.Y);

    for(uint32_t i = 0; i < EVMF_COUNT; ++i)
        mFeatureEnabled[i]=true;

    mInitMaterial2D.mAntiAliasing = EAAM_OFF;
    mInitMaterial2D.mLighting = false;
    mInitMaterial2D.mZWriteEnable = false;
    mInitMaterial2D.mZBuffer = ECF_NEVER;

    for(uint32_t i = 0; i < 8; ++i)
    {
        mInitMaterial2D.mTextureLayer[i].mBilinearFilter = false;
        mInitMaterial2D.mTextureLayer[i].mTextureWrapU = ETC_REPEAT;
        mInitMaterial2D.mTextureLayer[i].mTextureWrapV = ETC_REPEAT;
    }

    mOverrideMaterial2D = mInitMaterial2D;
}
#endif // _LYNX_WINDOWS_
#ifdef _LYNX_LINUX_
//! constructor
VideoManager_C::VideoManager_C(WindowManagerLinux_C* dev, const Myth::Math::Vector2u screenSize) :
    OpenGLExtensionHandler(dev->get_log_manager()),
    mDevice(dev),
    mTimeManager(dev->get_time_manager()),
    mLogManager(dev->get_log_manager()),
    mMeshManipulator(0),
    mViewPort(0,0,0,0),
    mScreenSize(screenSize),
    mPrimitivesDrawn(0),
    mMinVertexCountForVBO(500),
    mTextureCreationFlags(0),
    mOverrideMaterial2DEnabled(false),
    mAllowZWriteOnTransparent(false),
    mTransformation3DChanged(true),
    mCurrentRenderMode(ERM_NONE),
    mResetRenderStates(true),
    mAntiAlias(dev->mParameters.mAntiAliasing),
    mRenderTargetTexture(0),
    mCurrentRendertargetSize(0,0),
    mColorFormat(ECF_R8G8B8),
    mCurrentTarget(ERT_FRAME_BUFFER)
{
    set_fog();

    set_texture_creation_flag(ETCF_ALWAYS_32_BIT, true);
    set_texture_creation_flag(ETCF_CREATE_MIP_MAPS, true);

    mViewPort = Myth::Math::Rectanglei(0,0, screenSize.X, screenSize.Y);

    for(uint32_t i = 0; i < EVMF_COUNT; ++i)
        mFeatureEnabled[i]=true;

    mInitMaterial2D.mAntiAliasing = EAAM_OFF;
    mInitMaterial2D.mLighting = false;
    mInitMaterial2D.mZWriteEnable = false;
    mInitMaterial2D.mZBuffer = ECF_NEVER;

    for(uint32_t i = 0; i < 8; ++i)
    {
        mInitMaterial2D.mTextureLayer[i].mBilinearFilter = false;
        mInitMaterial2D.mTextureLayer[i].mTextureWrapU = ETC_REPEAT;
        mInitMaterial2D.mTextureLayer[i].mTextureWrapV = ETC_REPEAT;
    }

    mOverrideMaterial2D = mInitMaterial2D;
}
#endif // _LYNX_LINUX_

bool VideoManager_C::init_driver()
{
#ifdef _LYNX_WINDOWS_
    // Create a window to test antialiasing support
    const char* ClassName = __TEXT("GLCIrrDeviceWin32");
    HINSTANCE lhInstance = GetModuleHandle(0);

    // Register Class
    WNDCLASSEX wcex;
    wcex.cbSize        = sizeof(WNDCLASSEX);
    wcex.style         = CS_HREDRAW | CS_VREDRAW;
    wcex.lpfnWndProc   = (WNDPROC)DefWindowProc;
    wcex.cbClsExtra    = 0;
    wcex.cbWndExtra    = 0;
    wcex.hInstance     = lhInstance;
    wcex.hIcon         = NULL;
    wcex.hCursor       = LoadCursor(NULL, IDC_ARROW);
    wcex.hbrBackground  = (HBRUSH)(COLOR_WINDOW+1);
    wcex.lpszMenuName  = 0;
    wcex.lpszClassName = ClassName;
    wcex.hIconSm       = 0;
    wcex.hIcon         = 0;
    RegisterClassEx(&wcex);

    RECT clientSize;
    clientSize.top = 0;
    clientSize.left = 0;
    clientSize.right = mDevice->mParameters.mWindowSize.X;
    clientSize.bottom = mDevice->mParameters.mWindowSize.Y;

    DWORD style = WS_POPUP;
    if(!mDevice->mParameters.mFullscreen)
        style = WS_SYSMENU | WS_BORDER | WS_CAPTION | WS_CLIPCHILDREN | WS_CLIPSIBLINGS;

    AdjustWindowRect(&clientSize, style, FALSE);

    const int32_t realWidth = clientSize.right - clientSize.left;
    const int32_t realHeight = clientSize.bottom - clientSize.top;

    const int32_t windowLeft = (GetSystemMetrics(SM_CXSCREEN) - realWidth) / 2;
    const int32_t windowTop = (GetSystemMetrics(SM_CYSCREEN) - realHeight) / 2;

    HWND temporary_wnd=CreateWindow(ClassName, __TEXT(""), style, windowLeft,
                                    windowTop, realWidth, realHeight, NULL, NULL, lhInstance, NULL);

    if(!temporary_wnd)
    {
        mLogManager->log("Cannot create a temporary window.", ELL_ERROR);
        return false;
    }

    mHDc = GetDC(temporary_wnd);

    // Set up pixel format descriptor with desired parameters
    PIXELFORMATDESCRIPTOR pfd =
    {
        sizeof(PIXELFORMATDESCRIPTOR),             // Size Of This Pixel Format Descriptor
        1,                                         // Version Number
        PFD_DRAW_TO_WINDOW |                       // Format Must Support Window
        PFD_SUPPORT_OPENGL |                       // Format Must Support OpenGL
        (mDevice->mParameters.mDoublebuffer?PFD_DOUBLEBUFFER:0) | // Must Support Double Buffering
        (mDevice->mParameters.mStereobuffer?PFD_STEREO:0),        // Must Support Stereo Buffer
        PFD_TYPE_RGBA,                             // Request An RGBA Format
        mDevice->mParameters.mBits,                               // Select Our Color Depth
        0, 0, 0, 0, 0, 0,                          // Color Bits Ignored
        0,                                         // No Alpha Buffer
        0,                                         // Shift Bit Ignored
        0,                                         // No Accumulation Buffer
        0, 0, 0, 0,	                               // Accumulation Bits Ignored
        mDevice->mParameters.mZBufferBits,                        // Z-Buffer (Depth Buffer)
        mDevice->mParameters.mStencilbuffer ? 1 : 0,              // Stencil Buffer Depth
        0,                                         // No Auxiliary Buffer
        PFD_MAIN_PLANE,                            // Main Drawing Layer
        0,                                         // Reserved
        0, 0, 0                                    // Layer Masks Ignored
    };

    GLuint PixelFormat;

    for(uint32_t i=0; i<5; ++i)
    {
        if(i == 1)
        {
            if(mDevice->mParameters.mStencilbuffer)
            {
                mLogManager->log("Cannot create a GL device with stencil buffer, disabling stencil shadows.", ELL_WARNING);
                mDevice->mParameters.mStencilbuffer = false;
                pfd.cStencilBits = 0;
            }
            else
                continue;
        }
        else if(i == 2)
        {
            pfd.cDepthBits = 24;
        }
        if(i == 3)
        {
            if(mDevice->mParameters.mBits!=16)
                pfd.cDepthBits = 16;
            else
                continue;
        }
        else if(i == 4)
        {
            // try single buffer
            if(mDevice->mParameters.mDoublebuffer)
                pfd.dwFlags &= ~PFD_DOUBLEBUFFER;
            else
                continue;
        }
        else if(i == 5)
        {
            mLogManager->log("Cannot create a GL device context", "No suitable format for temporary window.", ELL_ERROR);
            ReleaseDC(temporary_wnd, mHDc);
            DestroyWindow(temporary_wnd);
            return false;
        }

        // choose pixelformat
        PixelFormat = ChoosePixelFormat(mHDc, &pfd);
        if(PixelFormat)
            break;
    }

    SetPixelFormat(mHDc, PixelFormat, &pfd);
    mHglrc = wglCreateContext(mHDc);
    if(!mHglrc)
    {
        mLogManager->log("Cannot create a temporary GL rendering context.", ELL_ERROR);
        ReleaseDC(temporary_wnd, mHDc);
        DestroyWindow(temporary_wnd);
        return false;
    }

    if(!wglMakeCurrent(mHDc, mHglrc))
    {
        mLogManager->log("Cannot activate a temporary GL rendering context.", ELL_ERROR);
        wglDeleteContext(mHglrc);
        ReleaseDC(temporary_wnd, mHDc);
        DestroyWindow(temporary_wnd);
        return false;
    }

#ifdef WGL_ARB_pixel_format
    PFNWGLCHOOSEPIXELFORMATARBPROC wglChoosePixelFormat_ARB = (PFNWGLCHOOSEPIXELFORMATARBPROC)wglGetProcAddress("wglChoosePixelFormatARB");
    if(wglChoosePixelFormat_ARB)
    {
        // This value determines the number of samples used for antialiasing
        // My experience is that 8 does not show a big
        // improvement over 4, but 4 shows a big improvement
        // over 2.

        if(mAntiAlias > 32)
            mAntiAlias = 32;

        float fAttributes[] = {0.0, 0.0};
        int32_t iAttributes[] =
        {
            WGL_DRAW_TO_WINDOW_ARB,GL_TRUE,
            WGL_SUPPORT_OPENGL_ARB,GL_TRUE,
            WGL_ACCELERATION_ARB,WGL_FULL_ACCELERATION_ARB,
            WGL_COLOR_BITS_ARB,(mDevice->mParameters.mBits==32) ? 24 : 15,
            WGL_ALPHA_BITS_ARB,(mDevice->mParameters.mBits==32) ? 8 : 1,
            WGL_DEPTH_BITS_ARB,mDevice->mParameters.mZBufferBits, // 10,11
            WGL_STENCIL_BITS_ARB,(mDevice->mParameters.mStencilbuffer) ? 1 : 0,
            WGL_DOUBLE_BUFFER_ARB,GL_TRUE,
            WGL_STEREO_ARB,GL_FALSE,
#ifdef WGL_ARB_multisample
            WGL_SAMPLE_BUFFERS_ARB, 1,
            WGL_SAMPLES_ARB,mAntiAlias, // 20,21
#elif defined(WGL_EXT_multisample)
            WGL_SAMPLE_BUFFERS_EXT, 1,
            WGL_SAMPLES_EXT,mAntiAlias, // 20,21
#elif defined(WGL_3DFX_multisample)
            WGL_SAMPLE_BUFFERS_3DFX, 1,
            WGL_SAMPLES_3DFX,mAntiAlias, // 20,21
#endif
            WGL_PIXEL_TYPE_ARB, WGL_TYPE_RGBA_ARB,
            // other possible values:
            // WGL_ARB_pixel_format_float: WGL_TYPE_RGBA_FLOAT_ARB
            // WGL_EXT_pixel_format_packed_float: WGL_TYPE_RGBA_UNSIGNED_FLOAT_EXT
#if 0
#ifdef WGL_EXT_framebuffer_sRGB
            WGL_FRAMEBUFFER_SRGB_CAPABLE_EXT, GL_FALSE,
#endif
#endif
            0,0
        };

        int32_t rv=0;
        // Try to get an acceptable pixel format
        while(rv==0 && iAttributes[21]>1)
        {
            int32_t pixelFormat=0;
            uint32_t numFormats=0;
            const int32_t valid = wglChoosePixelFormat_ARB(mHDc,iAttributes,fAttributes,1,&pixelFormat,&numFormats);

            if(valid && numFormats>0)
                rv = pixelFormat;
            else
                iAttributes[21] -= 1;
        }
        if(rv)
        {
            PixelFormat=rv;
            mAntiAlias=iAttributes[21];
        }
    }
    else
#endif
        mAntiAlias=0;

    wglMakeCurrent(mHDc, NULL);
    wglDeleteContext(mHglrc);
    ReleaseDC(temporary_wnd, mHDc);
    DestroyWindow(temporary_wnd);

    // get hdc
    mWindow = mDevice->mHWnd;
    mHDc = GetDC(mWindow);
    if(!mHDc)
    {
        mLogManager->log("Cannot create a GL device context.", ELL_ERROR);
        return false;
    }

    // search for pixel format the simple way
    if(mAntiAlias < 2)
    {
        for(uint32_t i=0; i<5; ++i)
        {
            if(i == 1)
            {
                if(mDevice->mParameters.mStencilbuffer)
                {
                    mLogManager->log("Cannot create a GL device with stencil buffer, disabling stencil shadows.", ELL_WARNING);
                    mDevice->mParameters.mStencilbuffer = false;
                    pfd.cStencilBits = 0;
                }
                else
                    continue;
            }
            else if(i == 2)
            {
                pfd.cDepthBits = 24;
            }
            if(i == 3)
            {
                if(mDevice->mParameters.mBits!=16)
                    pfd.cDepthBits = 16;
                else
                    continue;
            }
            else if(i == 4)
            {
                mLogManager->log("Cannot create a GL device context", "No suitable format.", ELL_ERROR);
                return false;
            }

            // choose pixelformat
            PixelFormat = ChoosePixelFormat(mHDc, &pfd);
            if(PixelFormat)
                break;
        }
    }

    // set pixel format
    if(!SetPixelFormat(mHDc, PixelFormat, &pfd))
    {
        mLogManager->log("Cannot set the pixel format.", ELL_ERROR);
        return false;
    }

    // create rendering context
#ifdef WGL_ARB_create_context
    PFNWGLCREATECONTEXTATTRIBSARBPROC wglCreateContextAttribs_ARB = (PFNWGLCREATECONTEXTATTRIBSARBPROC)wglGetProcAddress("wglCreateContextAttribsARB");
    if(wglCreateContextAttribs_ARB)
    {
        int iAttribs[] =
        {
            WGL_CONTEXT_MAJOR_VERSION_ARB, 3,
            WGL_CONTEXT_MINOR_VERSION_ARB, 1,
            0
        };
        mHglrc=wglCreateContextAttribs_ARB(mHDc, 0, iAttribs);
    }
    else
#endif
        mHglrc=wglCreateContext(mHDc);

    if(!mHglrc)
    {
        mLogManager->log("Cannot create a GL rendering context.", ELL_ERROR);
        return false;
    }

    // activate rendering context

    if(!wglMakeCurrent(mHDc, mHglrc))
    {
        mLogManager->log("Cannot activate GL rendering context", ELL_ERROR);
        wglDeleteContext(mHglrc);
        return false;
    }

    int pf = GetPixelFormat(mHDc);
    DescribePixelFormat(mHDc, pf, sizeof(PIXELFORMATDESCRIPTOR), &pfd);
    if(pfd.cAlphaBits != 0)
    {
        if(pfd.cRedBits == 8)
            mColorFormat = ECF_A8R8G8B8;
        else
            mColorFormat = ECF_A1R5G5B5;
    }
    else
    {
        if(pfd.cRedBits == 8)
            mColorFormat = ECF_R8G8B8;
        else
            mColorFormat = ECF_R5G6B5;
    }

    generic_driver_init(mDevice->mParameters.mWindowSize, true);

#ifdef WGL_EXT_swap_control
    PFNWGLSWAPINTERVALEXTPROC wglSwapIntervalEXT;
    // vsync extension
    wglSwapIntervalEXT = (PFNWGLSWAPINTERVALEXTPROC)wglGetProcAddress("wglSwapIntervalEXT");
    // set vsync
    if(wglSwapIntervalEXT)
        wglSwapIntervalEXT(mDevice->mParameters.mVerticalSync ? 1 : 0);
#endif
    return true;
#endif // _LYNX_WINDOWS_

#ifdef _LYNX_LINUX_
    mX11Context = glXGetCurrentContext();
    mX11Window = mDevice->mWindow;
    mDrawable = glXGetCurrentDrawable();
    mX11Display = glXGetCurrentDisplay();

    generic_driver_init(mDevice->mParameters.mWindowSize, true);

    // set vsync
    //TODO: Check GLX_EXT_swap_control and GLX_MESA_swap_control
#ifdef GLX_SGI_swap_control
    if(mDevice->mParameters.mVerticalSync && glxSwapIntervalSGI)
        glxSwapIntervalSGI(1);
    else
        mLogManager->log("Vsync not supported", ELL_WARNING);
#endif
    return true;
#endif // _LYNX_LINUX_
}

bool VideoManager_C::generic_driver_init(const Myth::Math::Vector2u screenSize, bool stencilBuffer)
{
    mName = "OpenGL ";
    mName.append((char*)glGetString(GL_VERSION));
    int32_t pos = mName.find_first_of(' ', 7);
    if(pos != -1)
        mName = mName.substr(0, pos);

    print_version();

    // print renderer information
    const GLubyte* renderer = glGetString(GL_RENDERER);
    const GLubyte* vendor = glGetString(GL_VENDOR);
    if(renderer && vendor)
    {
        mLogManager->log(reinterpret_cast<const char*>(renderer), reinterpret_cast<const char*>(vendor), ELL_INFORMATION);
        mVendorName = reinterpret_cast<const char*>(vendor);
    }

    uint32_t i;
    for(i=0; i<8; ++i)
        mCurrentTexture[i]=0;
    // load extensions
    initExtensions(stencilBuffer);
    //dump();
    if(query_feature(EVMF_ARB_GLSL))
    {
        char buf[32];
        const uint32_t maj = mShaderLanguageVersion/100;
        snprintf(buf, 32, "%u.%u", maj, mShaderLanguageVersion-maj*100);
        mLogManager->log("GLSL version", buf, ELL_INFORMATION);
    }
    else
        mLogManager->log("GLSL not available", ELL_INFORMATION);

    glPixelStorei(GL_PACK_ALIGNMENT, 1);

    // Reset The Current Viewport
    glViewport(0, 0, screenSize.X, screenSize.Y);

    mUserClipPlanes.reserve(mMaxUserClipPlanes);
    for(i=0; i<mMaxUserClipPlanes; ++i)
        mUserClipPlanes.push_back(UserClipPlane());

    for(i=0; i<ETS_COUNT; ++i)
        set_transform(static_cast<E_TRANSFORMATION_STATE>(i), Myth::Math::Matrix4());

    set_ambient_light(Colorf(0.0f,0.0f,0.0f,0.0f));

    if(mFeatureAvailable[LYNX_EXT_separate_specular_color])
        glLightModeli(GL_LIGHT_MODEL_COLOR_CONTROL, GL_SEPARATE_SPECULAR_COLOR);

    glLightModeli(GL_LIGHT_MODEL_LOCAL_VIEWER, 1);

    glClearDepth(1.0);
    glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST);
    glHint(GL_LINE_SMOOTH_HINT, GL_NICEST);
    glHint(GL_POINT_SMOOTH_HINT, GL_FASTEST);
    glDepthFunc(GL_LEQUAL);
    glFrontFace(GL_CW);

    // adjust flat coloring scheme to DirectX version
    extGlProvokingVertex(GL_FIRST_VERTEX_CONVENTION_EXT);

    // create material renderers
    create_material_renderers();

    // set the renderstates
    set_render_states_3d_mode();

    glAlphaFunc(GL_GREATER, 0.f);

    // set fog mode
    set_fog(mFogColor, mFogType, mFogStart, mFogEnd, mFogDensity, mPixelFog, mRangeFog);

    // create matrix for flipping textures
    mTextureFlipMatrix.buildTextureTransform(0.0f, Myth::Math::Vector2f(0,0), Myth::Math::Vector2f(0,1.0f), Myth::Math::Vector2f(1.0f,-1.0f));

    // We need to reset once more at the beginning of the first rendering.
    // This fixes problems with intermediate changes to the material during texture load.
    mResetRenderStates = true;
    clear_buffers(true, true, true, 0x0);

    return true;
}

//! destructor
VideoManager_C::~VideoManager_C()
{
    if(mMeshManipulator)
        mMeshManipulator->decrement_counter();

    delete_all_textures();

    // delete material renderers
    delete_material_renderers();

    // delete hardware mesh buffers
    remove_all_hardware_buffers();

    mRequestedLights.clear();

#ifdef _LYNX_WINDOWS_
    if(mHglrc)
    {
        if(!wglMakeCurrent(0, 0))
            mLogManager->log("Release of dc and rc failed.", ELL_WARNING);

        if(!wglDeleteContext(mHglrc))
            mLogManager->log("Release of rendering context failed.", ELL_WARNING);
    }
    else
        mLogManager->log("No rendering context to delete");

    if(mHDc)
        ReleaseDC(mWindow, mHDc);
#endif
}

void VideoManager_C::create_material_renderers()
{
    // create OpenGL material renderers

    add_and_drop_material_renderer(new MaterialRenderer_SOLID(this));
    add_and_drop_material_renderer(new MaterialRenderer_SOLID_2_LAYER(this));

    // add the same renderer for all lightmap types
    MaterialRenderer_LIGHTMAP* lmr = new MaterialRenderer_LIGHTMAP(this);
    add_material_renderer(lmr); // for EMT_LIGHTMAP:
    add_material_renderer(lmr); // for EMT_LIGHTMAP_ADD:
    add_material_renderer(lmr); // for EMT_LIGHTMAP_M2:
    add_material_renderer(lmr); // for EMT_LIGHTMAP_M4:
    add_material_renderer(lmr); // for EMT_LIGHTMAP_LIGHTING:
    add_material_renderer(lmr); // for EMT_LIGHTMAP_LIGHTING_M2:
    add_material_renderer(lmr); // for EMT_LIGHTMAP_LIGHTING_M4:
    lmr->decrement_counter();

    // add remaining material renderer
    add_and_drop_material_renderer(new MaterialRenderer_DETAIL_MAP(this));
    add_and_drop_material_renderer(new MaterialRenderer_SPHERE_MAP(this));
    add_and_drop_material_renderer(new MaterialRenderer_REFLECTION_2_LAYER(this));
    add_and_drop_material_renderer(new MaterialRenderer_TRANSPARENT_ADD_COLOR(this));
    add_and_drop_material_renderer(new MaterialRenderer_TRANSPARENT_ALPHA_CHANNEL(this));
    add_and_drop_material_renderer(new MaterialRenderer_TRANSPARENT_ALPHA_CHANNEL_REF(this));
    add_and_drop_material_renderer(new MaterialRenderer_TRANSPARENT_VERTEX_ALPHA(this));
    add_and_drop_material_renderer(new MaterialRenderer_TRANSPARENT_REFLECTION_2_LAYER(this));

    // add normal map renderers
    int32_t tmp = 0;
    MaterialRenderer* renderer = 0;
    renderer = new NormalMapRenderer(this, tmp, mMaterialRenderers[EMT_SOLID].mRenderer);
    renderer->decrement_counter();
    renderer = new NormalMapRenderer(this, tmp, mMaterialRenderers[EMT_TRANSPARENT_ADD_COLOR].mRenderer);
    renderer->decrement_counter();
    renderer = new NormalMapRenderer(this, tmp, mMaterialRenderers[EMT_TRANSPARENT_VERTEX_ALPHA].mRenderer);
    renderer->decrement_counter();

    // add parallax map renderers
    renderer = new ParallaxMapRenderer(this, tmp, mMaterialRenderers[EMT_SOLID].mRenderer);
    renderer->decrement_counter();
    renderer = new ParallaxMapRenderer(this, tmp, mMaterialRenderers[EMT_TRANSPARENT_ADD_COLOR].mRenderer);
    renderer->decrement_counter();
    renderer = new ParallaxMapRenderer(this, tmp, mMaterialRenderers[EMT_TRANSPARENT_VERTEX_ALPHA].mRenderer);
    renderer->decrement_counter();

    // add basic 1 texture blending
    add_and_drop_material_renderer(new MaterialRenderer_ONETEXTURE_BLEND(this));
}

//! deletes all textures
void VideoManager_C::delete_all_textures()
{
    // we need to remove previously set textures which might otherwise be kept in the
    // last set material member. Could be optimized to reduce state changes.
    set_material(Material());

    for(uint32_t i=0; i < mTextures.size(); ++i)
        mTextures[i].mSurface->decrement_counter();

    mTextures.clear();
}

// small helper function to create vertex buffer object adress offsets
static inline uint8_t* buffer_offset(const long offset)
{
    return ((uint8_t*)0 + offset);
}

//! clears the zbuffer and color buffer
void VideoManager_C::clear_buffers(bool backBuffer, bool zBuffer, bool stencilBuffer, Color color)
{
    GLbitfield mask = 0;
    if(backBuffer)
    {
        const float inv = 1.0f / 255.0f;
        glClearColor(color.get_red() * inv, color.get_green() * inv,
                     color.get_blue() * inv, color.get_alpha() * inv);

        mask |= GL_COLOR_BUFFER_BIT;
    }

    if(zBuffer)
    {
        glDepthMask(GL_TRUE);
        mLastMaterial.mZWriteEnable=true;
        mask |= GL_DEPTH_BUFFER_BIT;
    }

    if(stencilBuffer)
        mask |= GL_STENCIL_BUFFER_BIT;

    glClear(mask);
}

//! applications must call this method before performing any rendering. returns false if failed.
bool VideoManager_C::begin_scene(bool backBuffer, bool zBuffer, Color color, Myth::Math::Rectanglei* sourceRect)
{
    mPrimitivesDrawn = 0;

    clear_buffers(backBuffer, zBuffer, false, color);
    return true;
}

//! applications must call this method after performing any rendering. returns false if failed.
bool VideoManager_C::end_scene()
{
    mFPSCounter.register_frame(mTimeManager->get_real_time(), mPrimitivesDrawn);
    update_all_hardware_buffers();

    glFlush();

#ifdef _LYNX_WINDOWS_
    return SwapBuffers(mHDc) == TRUE;
#endif // _LYNX_WINDOWS_

#ifdef _LYNX_LINUX_
    glXSwapBuffers(mX11Display, mDrawable);
    return true;
#endif // LYNX_LINUX_
}

//! Disable a feature of the driver.
void VideoManager_C::set_feature(E_VIDEO_MANAGER_FEATURE feature, bool flag)
{
    mFeatureEnabled[feature]=!flag;
}

//! queries the features of the driver, returns true if feature is available
bool VideoManager_C::query_feature(E_VIDEO_MANAGER_FEATURE feature) const
{
    return OpenGLExtensionHandler::query_feature(feature);
}

//! sets transformation
void VideoManager_C::set_transform(E_TRANSFORMATION_STATE state, const Myth::Math::Matrix4 mat)
{
    mMatrices[state] = mat;
    mTransformation3DChanged = true;

    switch(state)
    {
    case ETS_VIEW:
    case ETS_WORLD:
    {
        // OpenGL only has a model matrix, view and world is not existent. so lets fake these two.
        glMatrixMode(GL_MODELVIEW);
        glLoadMatrixf((mMatrices[ETS_VIEW] * mMatrices[ETS_WORLD]).pointer());
        // we have to update the clip planes to the latest view matrix
        for(uint32_t i=0; i<mMaxUserClipPlanes; ++i)
            if(mUserClipPlanes[i].mEnabled)
                upload_clip_plane(i);
    }
    break;
    case ETS_PROJECTION:
    {
        GLfloat glmat[16];
        create_gl_matrix(glmat, mat);
        // flip z to compensate OpenGLs right-hand coordinate system
        glmat[12] *= -1.0f;
        glMatrixMode(GL_PROJECTION);
        glLoadMatrixf(glmat);
    }
    break;
    case ETS_COUNT:
        return;
    default:
    {
        const uint32_t i = state - ETS_TEXTURE_0;
        if(i >= 8)
            break;

        const bool isRTT = mMaterial.get_texture(i) && mMaterial.get_texture(i)->is_render_target();

        if(mMultiTextureExtension)
            extGlActiveTexture(GL_TEXTURE0_ARB + i);

        glMatrixMode(GL_TEXTURE);
        if(!isRTT && mat.isIdentity())
            glLoadIdentity();
        else
        {
            GLfloat glmat[16];
            if(isRTT)
                create_gl_texture_matrix(glmat, mat * mTextureFlipMatrix);
            else
                create_gl_texture_matrix(glmat, mat);

            glLoadMatrixf(glmat);
        }
        break;
    }
    }

}

//! Returns the transformation set by set_transform
const Myth::Math::Matrix4 VideoManager_C::get_transform(E_TRANSFORMATION_STATE state) const
{
    return mMatrices[state];
}

//! sets a material
void VideoManager_C::set_material(const Material material)
{
    mMaterial = material;
    mOverrideMaterial.apply(mMaterial);

    for(int32_t i = mMaxTextureUnits-1; i>= 0; --i)
    {
        set_active_texture(i, material.get_texture(i));
        set_transform((E_TRANSFORMATION_STATE)(ETS_TEXTURE_0 + i),
                     mMaterial.get_texture_matrix(i));
    }
}

//! Removes a texture from the texture cache and deletes it, freeing lot of
//! memory.
void VideoManager_C::remove_texture(Texture* texture)
{
    if(!texture)
        return;

    for(uint32_t i=0; i<mTextures.size(); ++i)
    {
        if(mTextures[i].mSurface == texture)
        {
            texture->decrement_counter();
            mTextures.erase(mTextures.begin() + i);
        }
    }
}

//! Removes all texture from the texture cache and deletes them, freeing lot of
//! memory.
void VideoManager_C::remove_all_textures()
{
    set_material(Material());
    delete_all_textures();
}

//! Returns a texture by index
Texture* VideoManager_C::get_texture_by_index(uint32_t i)
{
    if(i < mTextures.size())
        return mTextures[i].mSurface;

    return 0;
}

//! Returns amount of textures currently loaded
uint32_t VideoManager_C::get_texture_count() const
{
    return mTextures.size();
}

//! Renames a texture
void VideoManager_C::rename_texture(Texture* texture, std::string newName)
{
    // we can do a const_cast here safely, the name of the ITexture interface
    // is just readonly to prevent the user changing the texture name without invoking
    // this method, because the textures will need resorting afterwards

    texture->set_name(newName);
    sort(mTextures.begin(), mTextures.end());
}

//! loads a Texture
Texture* VideoManager_C::get_texture(const std::string filename)
{
    Texture* texture = find_texture(filename);
    if(texture)
        return texture;
    else
        return 0;
}

//! adds a surface, not loaded or created by the Lynx Engine
void VideoManager_C::add_texture(Texture* texture)
{
    if(texture)
    {
        Surface s;
        s.mSurface = texture;
        texture->increment_counter();

        mTextures.push_back(s);

        // the new texture is now at the end of the texture list. when searching for
        // the next new texture, the texture array will be sorted and the index of this texture
        // will be changed. to let the order be more consistent to the user, sort
        // the textures now already although this isn't necessary:

        sort(mTextures.begin(), mTextures.end());
    }
}

//! looks if the image is already loaded
Texture* VideoManager_C::find_texture(const std::string filename)
{

    for(uint32_t i = 0; i < mTextures.size(); i++)
        if(mTextures[i].mSurface->get_name() == filename)
            return mTextures[i].mSurface;

    return 0;
}

//! Creates a texture from a loaded IImage.
Texture* VideoManager_C::add_texture(const std::string name, Image* image, void* mipmapData)
{
    if(0 == name.size() || !image)
        return 0;

    Texture* t = new Texture_C(image, name, mipmapData, this);
    if(t)
    {
        add_texture(t);
        t->decrement_counter();
    }
    return t;
}

//! creates a Texture
Texture* VideoManager_C::add_texture(const Myth::Math::Vector2u size,
                                    const std::string name, E_COLOR_FORMAT format)
{
    if(is_render_target_only_format(format))
    {
        mLogManager->log("Could not create Texture, format only supported for render target textures.", ELL_WARNING);
        return 0;
    }

    if(0 == name.size())
        return 0;

    Image* image = new Image_C(format, size);
    Texture* t = new Texture_C(image, name, 0, this);
    image->decrement_counter();
    add_texture(t);

    if(t)
        t->decrement_counter();

    return t;
}

//! set or reset special render targets
bool VideoManager_C::set_render_target(E_RENDER_TARGET target, bool clearTarget,
                                     bool clear_z_buffer, Color color)
{
    if(target != mCurrentTarget)
        set_render_target(0, false, false, 0x0);

    if(ERT_RENDER_TEXTURE == target)
    {
        mLogManager->log("Fatal Error: For render textures call set_render_target with the actual texture as first parameter.", ELL_ERROR);
        return false;
    }

    glDrawBuffer(GL_BACK_LEFT);

    // exit with false, but also with working color buffer
    if(target != ERT_FRAME_BUFFER)
        return false;

    mCurrentTarget=target;
    clear_buffers(clearTarget, clear_z_buffer, false, color);
    return true;
}

//! sets a render target
bool VideoManager_C::set_render_target(Texture* texture, bool clearBackBuffer,
                                     bool clear_z_buffer, Color color)
{
    // check if we should set the previous RT back

    set_active_texture(0, 0);
    mResetRenderStates=true;
    if(mRenderTargetTexture!=0)
    {
        mRenderTargetTexture->unbindRTT();
    }

    if(texture)
    {
        // we want to set a new target. so do this.
        glViewport(0, 0, texture->get_size().X, texture->get_size().Y);
        mRenderTargetTexture = static_cast<Texture_C*>(texture);
        mRenderTargetTexture->bindRTT();
        mCurrentRendertargetSize = texture->get_size();
        mCurrentTarget=ERT_RENDER_TEXTURE;
    }
    else
    {
        glViewport(0,0,mScreenSize.X,mScreenSize.Y);
        mRenderTargetTexture = 0;
        mCurrentRendertargetSize = Myth::Math::Vector2u(0,0);
        mCurrentTarget=ERT_FRAME_BUFFER;
        glDrawBuffer(GL_BACK_LEFT);
    }

    clear_buffers(clearBackBuffer, clear_z_buffer, false, color);
    return true;
}

//! Sets multiple render targets
bool VideoManager_C::set_render_target(const std::vector<RenderTarget> targets,
                                     bool clearBackBuffer, bool clear_z_buffer, Color color)
{
    if(targets.size()==0)
        return set_render_target(0, clearBackBuffer, clear_z_buffer, color);

    uint32_t maxMultipleRTTs = Myth::Math::min(static_cast<uint32_t>(mMaxMultipleRenderTargets), (uint32_t)targets.size());

    // determine common size
    Myth::Math::Vector2u rttSize = mCurrentRendertargetSize;
    if(targets[0].mTargetType==ERT_RENDER_TEXTURE)
    {
        if(!targets[0].mRenderTexture)
        {
            mLogManager->log("Missing render texture for MRT.", ELL_ERROR);
            return false;
        }
        rttSize=targets[0].mRenderTexture->get_size();
    }

    for(uint32_t i = 0; i < maxMultipleRTTs; ++i)
    {
        // check for right driver type
        if(targets[i].mTargetType==ERT_RENDER_TEXTURE)
        {
            if(!targets[i].mRenderTexture)
            {
                maxMultipleRTTs=i;
                mLogManager->log("Missing render texture for MRT.", ELL_WARNING);
                break;
            }

            // check for valid render target
            if(!targets[i].mRenderTexture->is_render_target() || !static_cast<Texture_C*>(targets[i].mRenderTexture)->isFrameBufferObject())
            {
                maxMultipleRTTs=i;
                mLogManager->log("Tried to set a non FBO-RTT as render target.", ELL_WARNING);
                break;
            }

            // check for valid size
            if(rttSize != targets[i].mRenderTexture->get_size())
            {
                maxMultipleRTTs=i;
                mLogManager->log("Render target texture has wrong size.", ELL_WARNING);
                break;
            }
        }
    }
    if(maxMultipleRTTs==0)
    {
        mLogManager->log("No valid MRTs.", ELL_ERROR);
        return false;
    }

    // init FBO, if any
    for(uint32_t i=0; i<maxMultipleRTTs; ++i)
    {
        if(targets[i].mTargetType==ERT_RENDER_TEXTURE)
        {
            set_render_target(targets[i].mRenderTexture, false, false, 0x0);
            break;
        }
    }
    // init other main buffer, if necessary
    if(targets[0].mTargetType!=ERT_RENDER_TEXTURE)
        set_render_target(targets[0].mTargetType, false, false, 0x0);

    // attach other textures and store buffers into array
    if(maxMultipleRTTs > 1)
    {
        mCurrentTarget=ERT_MULTI_RENDER_TEXTURES;
        std::vector<GLenum> MRTs;
        MRTs.reserve(maxMultipleRTTs);
        for(uint32_t i = 0; i < maxMultipleRTTs; i++)
        {
            if(mFeatureAvailable[LYNX_EXT_draw_buffers2])
            {
                extGlColorMaskIndexed(i,
                                      (targets[i].mColorMask & ECP_RED)?GL_TRUE:GL_FALSE,
                                      (targets[i].mColorMask & ECP_GREEN)?GL_TRUE:GL_FALSE,
                                      (targets[i].mColorMask & ECP_BLUE)?GL_TRUE:GL_FALSE,
                                      (targets[i].mColorMask & ECP_ALPHA)?GL_TRUE:GL_FALSE);
                if(targets[i].mBlendEnable)
                    extGlEnableIndexed(GL_BLEND, i);
                else
                    extGlDisableIndexed(GL_BLEND, i);
            }
            if(mFeatureAvailable[LYNX_AMD_draw_buffers_blend] || mFeatureAvailable[LYNX_ARB_draw_buffers_blend])
            {
                extGlBlendFuncIndexed(i, targets[i].mBlendFuncSrc, targets[i].mBlendFuncDst);
            }
            if(targets[i].mTargetType==ERT_RENDER_TEXTURE)
            {
                GLenum attachment = GL_NONE;
                // attach texture to FrameBuffer Object on Color [i]
                attachment = GL_COLOR_ATTACHMENT0_EXT+i;
                extGlFramebufferTexture2D(GL_FRAMEBUFFER_EXT, attachment, GL_TEXTURE_2D, static_cast<Texture_C*>(targets[i].mRenderTexture)->getOpenGLTextureName(), 0);
                MRTs[i]=attachment;
            }
            else
                MRTs[i]=GL_BACK_LEFT; // main buffer
        }

        extGlDrawBuffers(maxMultipleRTTs, &MRTs[0]);
    }

    clear_buffers(clearBackBuffer, clear_z_buffer, false, color);
    return true;
}

//! sets a viewport
void VideoManager_C::set_view_port(const Myth::Math::Rectanglei area)
{
    Myth::Math::Rectanglei vp = area;
    Myth::Math::Rectanglei rendert(0,0, get_current_render_target_size().X, get_current_render_target_size().Y);
    vp.clip_against(rendert);

    if(vp.get_height()>0 && vp.get_width()>0)
        glViewport(vp.UpperLeftCorner.X,
                   get_current_render_target_size().Y - vp.UpperLeftCorner.Y - vp.get_height(),
                   vp.get_width(), vp.get_height());

    mViewPort = vp;
}

//! gets the area of the current viewport
const Myth::Math::Rectanglei VideoManager_C::get_view_port() const
{
    return mViewPort;
}

//! draws a vertex primitive list
void VideoManager_C::draw_vertex_primitive_list(const void* vertices, uint32_t vertexCount, const void* indexList, uint32_t primitiveCount, E_VERTEX_TYPE vType, E_PRIMITIVE_TYPE pType, E_INDEX_TYPE iType)
{
    if(!primitiveCount || !vertexCount)
        return;

    if(!check_primitive_count(primitiveCount))
        return;

    if((iType==EIT_16BIT) && (vertexCount>65536))
        mLogManager->log("Too many vertices for 16bit index type, render artifacts may occur.");
    mPrimitivesDrawn += primitiveCount;

    if(vertices)
        create_color_buffer(vertices, vertexCount, vType);

    // draw everything
    set_render_states_3d_mode();

    if(mMultiTextureExtension)
        extGlClientActiveTexture(GL_TEXTURE0_ARB);

    glEnableClientState(GL_COLOR_ARRAY);
    glEnableClientState(GL_VERTEX_ARRAY);
    if((pType!=EPT_POINTS) && (pType!=EPT_POINT_SPRITES))
        glEnableClientState(GL_TEXTURE_COORD_ARRAY);
    if((pType!=EPT_POINTS) && (pType!=EPT_POINT_SPRITES))
        glEnableClientState(GL_NORMAL_ARRAY);

    if(vertices)
        glColorPointer(4, GL_UNSIGNED_BYTE, 0, &mColorBuffer[0]);

    switch(vType)
    {
    case EVT_STANDARD:
        if(vertices)
        {
            glNormalPointer(GL_FLOAT, sizeof(Vertex), &(static_cast<const Vertex*>(vertices))[0].mNormal);
            glTexCoordPointer(2, GL_FLOAT, sizeof(Vertex), &(static_cast<const Vertex*>(vertices))[0].mTCoords);
            glVertexPointer(3, GL_FLOAT, sizeof(Vertex), &(static_cast<const Vertex*>(vertices))[0].mPos);
        }
        else
        {
            glNormalPointer(GL_FLOAT, sizeof(Vertex), buffer_offset(12));
            glColorPointer(4, GL_UNSIGNED_BYTE, sizeof(Vertex), buffer_offset(24));
            glTexCoordPointer(2, GL_FLOAT, sizeof(Vertex), buffer_offset(28));
            glVertexPointer(3, GL_FLOAT, sizeof(Vertex), 0);
        }

        if(mMultiTextureExtension && mCurrentTexture[1])
        {
            extGlClientActiveTexture(GL_TEXTURE1_ARB);
            glEnableClientState(GL_TEXTURE_COORD_ARRAY);
            if(vertices)
                glTexCoordPointer(2, GL_FLOAT, sizeof(Vertex), &(static_cast<const Vertex*>(vertices))[0].mTCoords);
            else
                glTexCoordPointer(2, GL_FLOAT, sizeof(Vertex), buffer_offset(28));
        }
        break;
    case EVT_2TCOORDS:
        if(vertices)
        {
            glNormalPointer(GL_FLOAT, sizeof(Vertex2TCoords), &(static_cast<const Vertex2TCoords*>(vertices))[0].mNormal);
            glTexCoordPointer(2, GL_FLOAT, sizeof(Vertex2TCoords), &(static_cast<const Vertex2TCoords*>(vertices))[0].mTCoords);
            glVertexPointer(3, GL_FLOAT, sizeof(Vertex2TCoords), &(static_cast<const Vertex2TCoords*>(vertices))[0].mPos);
        }
        else
        {
            glNormalPointer(GL_FLOAT, sizeof(Vertex2TCoords), buffer_offset(12));
            glColorPointer(4, GL_UNSIGNED_BYTE, sizeof(Vertex2TCoords), buffer_offset(24));
            glTexCoordPointer(2, GL_FLOAT, sizeof(Vertex2TCoords), buffer_offset(28));
            glVertexPointer(3, GL_FLOAT, sizeof(Vertex2TCoords), buffer_offset(0));
        }


        if(mMultiTextureExtension)
        {
            extGlClientActiveTexture(GL_TEXTURE1_ARB);
            glEnableClientState(GL_TEXTURE_COORD_ARRAY);
            if(vertices)
                glTexCoordPointer(2, GL_FLOAT, sizeof(Vertex2TCoords), &(static_cast<const Vertex2TCoords*>(vertices))[0].mTCoords2);
            else
                glTexCoordPointer(2, GL_FLOAT, sizeof(Vertex2TCoords), buffer_offset(36));
        }
        break;
    case EVT_TANGENTS:
        if(vertices)
        {
            glNormalPointer(GL_FLOAT, sizeof(VertexTangents), &(static_cast<const VertexTangents*>(vertices))[0].mNormal);
            glTexCoordPointer(2, GL_FLOAT, sizeof(VertexTangents), &(static_cast<const VertexTangents*>(vertices))[0].mTCoords);
            glVertexPointer(3, GL_FLOAT, sizeof(VertexTangents), &(static_cast<const VertexTangents*>(vertices))[0].mPos);
        }
        else
        {
            glNormalPointer(GL_FLOAT, sizeof(VertexTangents), buffer_offset(12));
            glColorPointer(4, GL_UNSIGNED_BYTE, sizeof(VertexTangents), buffer_offset(24));
            glTexCoordPointer(2, GL_FLOAT, sizeof(VertexTangents), buffer_offset(28));
            glVertexPointer(3, GL_FLOAT, sizeof(VertexTangents), buffer_offset(0));
        }

        if(mMultiTextureExtension)
        {
            extGlClientActiveTexture(GL_TEXTURE1_ARB);
            glEnableClientState(GL_TEXTURE_COORD_ARRAY);
            if(vertices)
                glTexCoordPointer(3, GL_FLOAT, sizeof(VertexTangents), &(static_cast<const VertexTangents*>(vertices))[0].mTangent);
            else
                glTexCoordPointer(3, GL_FLOAT, sizeof(VertexTangents), buffer_offset(36));

            extGlClientActiveTexture(GL_TEXTURE2_ARB);
            glEnableClientState(GL_TEXTURE_COORD_ARRAY);
            if(vertices)
                glTexCoordPointer(3, GL_FLOAT, sizeof(VertexTangents), &(static_cast<const VertexTangents*>(vertices))[0].mBinormal);
            else
                glTexCoordPointer(3, GL_FLOAT, sizeof(VertexTangents), buffer_offset(48));
        }
        break;
    }

    render_array(indexList, primitiveCount, pType, iType);

    if(mMultiTextureExtension)
    {
        if(vType==EVT_TANGENTS)
        {
            extGlClientActiveTexture(GL_TEXTURE2_ARB);
            glDisableClientState(GL_TEXTURE_COORD_ARRAY);
        }
        if((vType!=EVT_STANDARD) || mCurrentTexture[1])
        {
            extGlClientActiveTexture(GL_TEXTURE1_ARB);
            glDisableClientState(GL_TEXTURE_COORD_ARRAY);
        }
        extGlClientActiveTexture(GL_TEXTURE0_ARB);
    }
    glDisableClientState(GL_COLOR_ARRAY);
    glDisableClientState(GL_VERTEX_ARRAY);
    glDisableClientState(GL_NORMAL_ARRAY);
    glDisableClientState(GL_TEXTURE_COORD_ARRAY);
}

void VideoManager_C::create_color_buffer(const void* vertices, uint32_t vertexCount, E_VERTEX_TYPE vType)
{
    // convert colors to gl color format.
    vertexCount *= 4; //reused as color component count
    mColorBuffer.reserve(vertexCount);
    uint32_t i;

    switch(vType)
    {
    case EVT_STANDARD:
    {
        const Vertex* p = static_cast<const Vertex*>(vertices);
        for(i=0; i<vertexCount; i+=4)
        {
            p->mColor.to_opengl_color(&mColorBuffer[i]);
            ++p;
        }
    }
    break;
    case EVT_2TCOORDS:
    {
        const Vertex2TCoords* p = static_cast<const Vertex2TCoords*>(vertices);
        for(i=0; i<vertexCount; i+=4)
        {
            p->mColor.to_opengl_color(&mColorBuffer[i]);
            ++p;
        }
    }
    break;
    case EVT_TANGENTS:
    {
        const VertexTangents* p = static_cast<const VertexTangents*>(vertices);
        for(i=0; i<vertexCount; i+=4)
        {
            p->mColor.to_opengl_color(&mColorBuffer[i]);
            ++p;
        }
    }
    break;
    }
}


void VideoManager_C::render_array(const void* indexList, uint32_t primitiveCount, E_PRIMITIVE_TYPE pType, E_INDEX_TYPE iType)
{
    GLenum indexSize=0;

    switch(iType)
    {
    case EIT_16BIT:
    {
        indexSize=GL_UNSIGNED_SHORT;
        break;
    }
    case EIT_32BIT:
    {
        indexSize=GL_UNSIGNED_INT;
        break;
    }
    }

    switch(pType)
    {
    case EPT_POINTS:
    case EPT_POINT_SPRITES:
    {
        if(pType==EPT_POINT_SPRITES && mFeatureAvailable[LYNX_ARB_point_sprite])
            glEnable(GL_POINT_SPRITE_ARB);

        // prepare size and attenuation (where supported)
        GLfloat particleSize=mMaterial.mThickness;
//			if (AntiAlias)
//				particleSize=Myth::Math::clamp(particleSize, DimSmoothedPoint[0], DimSmoothedPoint[1]);
//			else
        particleSize=Myth::Math::clamp(particleSize, mDimAliasedPoint[0], mDimAliasedPoint[1]);
        const float att[] = {1.0f, 1.0f, 0.0f};
        extGlPointParameterfv(GL_POINT_DISTANCE_ATTENUATION, att);
//			extGlPointParameterf(GL_POINT_SIZE_MIN,1.f);
        extGlPointParameterf(GL_POINT_SIZE_MAX, particleSize);
        extGlPointParameterf(GL_POINT_FADE_THRESHOLD_SIZE, 1.0f);

        glPointSize(particleSize);

        if(pType==EPT_POINT_SPRITES && mFeatureAvailable[LYNX_ARB_point_sprite])
            glTexEnvf(GL_POINT_SPRITE_ARB,GL_COORD_REPLACE, GL_TRUE);
        glDrawArrays(GL_POINTS, 0, primitiveCount);
        if(pType==EPT_POINT_SPRITES && mFeatureAvailable[LYNX_ARB_point_sprite])
        {
            glDisable(GL_POINT_SPRITE_ARB);
            glTexEnvf(GL_POINT_SPRITE_ARB,GL_COORD_REPLACE, GL_FALSE);
        }
    }
    break;
    case EPT_LINE_STRIP:
        glDrawElements(GL_LINE_STRIP, primitiveCount+1, indexSize, indexList);
        break;
    case EPT_LINE_LOOP:
        glDrawElements(GL_LINE_LOOP, primitiveCount, indexSize, indexList);
        break;
    case EPT_LINES:
        glDrawElements(GL_LINES, primitiveCount*2, indexSize, indexList);
        break;
    case EPT_TRIANGLE_STRIP:
        glDrawElements(GL_TRIANGLE_STRIP, primitiveCount+2, indexSize, indexList);
        break;
    case EPT_TRIANGLE_FAN:
        glDrawElements(GL_TRIANGLE_FAN, primitiveCount+2, indexSize, indexList);
        break;
    case EPT_TRIANGLES:
        glDrawElements(GL_TRIANGLES, primitiveCount*3, indexSize, indexList);
        break;
    case EPT_QUAD_STRIP:
        glDrawElements(GL_QUAD_STRIP, primitiveCount*2+2, indexSize, indexList);
        break;
    case EPT_QUADS:
        glDrawElements(GL_QUADS, primitiveCount*4, indexSize, indexList);
        break;
    case EPT_POLYGON:
        glDrawElements(GL_POLYGON, primitiveCount, indexSize, indexList);
        break;
    }
}

//! draws a vertex primitive list in 2d
void VideoManager_C::draw_2d_vertex_primitive_list(const void* vertices, uint32_t vertexCount, const void* indexList, uint32_t primitiveCount, E_VERTEX_TYPE vType, E_PRIMITIVE_TYPE pType, E_INDEX_TYPE iType)
{
    if(!primitiveCount || !vertexCount)
        return;

    if(!check_primitive_count(primitiveCount))
        return;


    if((iType==EIT_16BIT) && (vertexCount>65536))
        mLogManager->log("Too many vertices for 16bit index type, render artifacts may occur.");
    mPrimitivesDrawn += primitiveCount;

    if(vertices)
        create_color_buffer(vertices, vertexCount, vType);

    // draw everything
    this->set_active_texture(0, mMaterial.get_texture(0));
    if(mMaterial.mMaterialType==EMT_ONETEXTURE_BLEND)
    {
        E_BLEND_FACTOR srcFact;
        E_BLEND_FACTOR dstFact;
        E_MODULATE_FUNC modulo;
        uint32_t alphaSource;
        unpack_texture_blend_func(srcFact, dstFact, modulo, alphaSource, mMaterial.mMaterialTypeParam);
        set_render_states_2d_mode(alphaSource&EAS_VERTEX_COLOR, (mMaterial.get_texture(0) != 0), (alphaSource&EAS_TEXTURE) != 0);
    }
    else
        set_render_states_2d_mode(mMaterial.mMaterialType==EMT_TRANSPARENT_VERTEX_ALPHA, (mMaterial.get_texture(0) != 0), mMaterial.mMaterialType==EMT_TRANSPARENT_ALPHA_CHANNEL);

    if(mMultiTextureExtension)
        extGlClientActiveTexture(GL_TEXTURE0_ARB);

    glEnableClientState(GL_COLOR_ARRAY);
    glEnableClientState(GL_VERTEX_ARRAY);
    if((pType!=EPT_POINTS) && (pType!=EPT_POINT_SPRITES))
        glEnableClientState(GL_TEXTURE_COORD_ARRAY);

    if(vertices)
        glColorPointer(4, GL_UNSIGNED_BYTE, 0, &mColorBuffer[0]);

    switch(vType)
    {
    case EVT_STANDARD:
        if(vertices)
        {
            glTexCoordPointer(2, GL_FLOAT, sizeof(Vertex), &(static_cast<const Vertex*>(vertices))[0].mTCoords);
            glVertexPointer(2, GL_FLOAT, sizeof(Vertex), &(static_cast<const Vertex*>(vertices))[0].mPos);
        }
        else
        {
            glColorPointer(4, GL_UNSIGNED_BYTE, sizeof(Vertex), buffer_offset(24));
            glTexCoordPointer(2, GL_FLOAT, sizeof(Vertex), buffer_offset(28));
            glVertexPointer(2, GL_FLOAT, sizeof(Vertex), 0);
        }

        if(mMultiTextureExtension && mCurrentTexture[1])
        {
            extGlClientActiveTexture(GL_TEXTURE1_ARB);
            glEnableClientState(GL_TEXTURE_COORD_ARRAY);
            if(vertices)
                glTexCoordPointer(2, GL_FLOAT, sizeof(Vertex), &(static_cast<const Vertex*>(vertices))[0].mTCoords);
            else
                glTexCoordPointer(2, GL_FLOAT, sizeof(Vertex), buffer_offset(28));
        }
        break;
    case EVT_2TCOORDS:
        if(vertices)
        {
            glTexCoordPointer(2, GL_FLOAT, sizeof(Vertex2TCoords), &(static_cast<const Vertex2TCoords*>(vertices))[0].mTCoords);
            glVertexPointer(2, GL_FLOAT, sizeof(Vertex2TCoords), &(static_cast<const Vertex2TCoords*>(vertices))[0].mPos);
        }
        else
        {
            glColorPointer(4, GL_UNSIGNED_BYTE, sizeof(Vertex2TCoords), buffer_offset(24));
            glTexCoordPointer(2, GL_FLOAT, sizeof(Vertex2TCoords), buffer_offset(28));
            glVertexPointer(2, GL_FLOAT, sizeof(Vertex2TCoords), buffer_offset(0));
        }

        if(mMultiTextureExtension)
        {
            extGlClientActiveTexture(GL_TEXTURE1_ARB);
            glEnableClientState(GL_TEXTURE_COORD_ARRAY);
            if(vertices)
                glTexCoordPointer(2, GL_FLOAT, sizeof(Vertex2TCoords), &(static_cast<const Vertex2TCoords*>(vertices))[0].mTCoords2);
            else
                glTexCoordPointer(2, GL_FLOAT, sizeof(Vertex2TCoords), buffer_offset(36));
        }
        break;
    case EVT_TANGENTS:
        if(vertices)
        {
            glTexCoordPointer(2, GL_FLOAT, sizeof(VertexTangents), &(static_cast<const VertexTangents*>(vertices))[0].mTCoords);
            glVertexPointer(2, GL_FLOAT, sizeof(VertexTangents), &(static_cast<const VertexTangents*>(vertices))[0].mPos);
        }
        else
        {
            glColorPointer(4, GL_UNSIGNED_BYTE, sizeof(VertexTangents), buffer_offset(24));
            glTexCoordPointer(2, GL_FLOAT, sizeof(VertexTangents), buffer_offset(28));
            glVertexPointer(2, GL_FLOAT, sizeof(VertexTangents), buffer_offset(0));
        }

        break;
    }

    render_array(indexList, primitiveCount, pType, iType);

    if(mMultiTextureExtension)
    {
        if((vType!=EVT_STANDARD) || mCurrentTexture[1])
        {
            extGlClientActiveTexture(GL_TEXTURE1_ARB);
            glDisableClientState(GL_TEXTURE_COORD_ARRAY);
        }
        extGlClientActiveTexture(GL_TEXTURE0_ARB);
    }
    glDisableClientState(GL_COLOR_ARRAY);
    glDisableClientState(GL_VERTEX_ARRAY);
    glDisableClientState(GL_TEXTURE_COORD_ARRAY);
}

//! Draws a 3d line.
void VideoManager_C::draw_3d_line(const Myth::Math::Vector3f start, const Myth::Math::Vector3f end, Color color)
{
    set_render_states_3d_mode();

    glBegin(GL_LINES);
    glColor4ub(color.get_red(), color.get_green(), color.get_blue(), color.get_alpha());
    glVertex3f(start.X, start.Y, start.Z);

    glVertex3f(end.X, end.Y, end.Z);
    glEnd();
}

//! Draws a 3d triangle.
void VideoManager_C::draw_3d_triangle(const Myth::Math::Triangle3f triangle, Color color)
{
    Vertex vertices[3];
    vertices[0].mPos=triangle.mPointA;
    vertices[0].mColor=color;
    vertices[0].mNormal=triangle.get_normal().normalize();
    vertices[0].mTCoords.set(0.f,0.f);
    vertices[1].mPos=triangle.mPointB;
    vertices[1].mColor=color;
    vertices[1].mNormal=vertices[0].mNormal;
    vertices[1].mTCoords.set(0.5f,1.f);
    vertices[2].mPos=triangle.mPointC;
    vertices[2].mColor=color;
    vertices[2].mNormal=vertices[0].mNormal;
    vertices[2].mTCoords.set(1.f,0.f);
    const uint16_t indexList[] = {0,1,2};
    draw_vertex_primitive_list(vertices, 3, indexList, 1, EVT_STANDARD, EPT_TRIANGLES, EIT_16BIT);
}

//! Draws a 3d axis aligned box.
void VideoManager_C::draw_3d_box(const Myth::Math::BoundingBox3f box, Color color)
{
    Myth::Math::Vector3f edges[8];
    box.getEdges(edges);

    // TODO: optimize into one big drawIndexPrimitive call.

    draw_3d_line(edges[5], edges[1], color);
    draw_3d_line(edges[1], edges[3], color);
    draw_3d_line(edges[3], edges[7], color);
    draw_3d_line(edges[7], edges[5], color);
    draw_3d_line(edges[0], edges[2], color);
    draw_3d_line(edges[2], edges[6], color);
    draw_3d_line(edges[6], edges[4], color);
    draw_3d_line(edges[4], edges[0], color);
    draw_3d_line(edges[1], edges[0], color);
    draw_3d_line(edges[3], edges[2], color);
    draw_3d_line(edges[7], edges[6], color);
    draw_3d_line(edges[5], edges[4], color);
}

//! draws an 2d image
void VideoManager_C::draw_2d_image(const Texture* texture, const Myth::Math::Vector2i destPos)
{
    if(!texture)
        return;

    draw_2d_image(texture, destPos, Myth::Math::Rectanglei(0, 0, texture->get_original_size().X, texture->get_original_size().Y));
}

//! draws a set of 2d images, using a color and the alpha channel of the
//! texture if desired. The images are drawn beginning at pos and concatenated
//! in one line. All drawings are clipped against clipRect (if != 0).
//! The subtextures are defined by the array of sourceRects and are chosen
//! by the indices given.
void VideoManager_C::draw_2d_image_batch(const Texture* texture,
                                      const Myth::Math::Vector2i pos,
                                      const std::vector<Myth::Math::Rectanglei> sourceRects,
                                      const std::vector<int32_t> indices,
                                      int32_t kerningWidth,
                                      Color color,
                                      bool useAlphaChannelOfTexture)
{
    Myth::Math::Vector2i target(pos);

    for(uint32_t i=0; i<indices.size(); ++i)
    {
        draw_2d_image(texture, target, sourceRects[indices[i]], color, useAlphaChannelOfTexture);
        target.X += sourceRects[indices[i]].get_width();
        target.X += kerningWidth;
    }
}

//! draws a set of 2d images, using a color and the alpha channel of the
//! texture if desired.
void VideoManager_C::draw_2d_image_batch(const Texture* texture,
                                      const std::vector<Myth::Math::Vector2i> positions,
                                      const std::vector<Myth::Math::Rectanglei> sourceRects,
                                      Color color,
                                      bool useAlphaChannelOfTexture)
{
    if(!texture)
        return;

    const uint32_t drawCount = Myth::Math::min<uint32_t>(positions.size(), sourceRects.size());

    // texcoords need to be flipped horizontally for RTTs
    const bool isRTT = texture->is_render_target();
    const Myth::Math::Vector2u ss = texture->get_original_size();
    const float invW = 1.f / static_cast<float>(ss.X);
    const float invH = 1.f / static_cast<float>(ss.Y);
    const Myth::Math::Vector2u renderTargetSize = get_current_render_target_size();

    disable_textures(1);
    if(!set_active_texture(0, texture))
        return;
    set_render_states_2d_mode(color.get_alpha()<255, true, useAlphaChannelOfTexture);

    glColor4ub(color.get_red(), color.get_green(), color.get_blue(), color.get_alpha());
    glBegin(GL_QUADS);

    for(uint32_t i=0; i<drawCount; ++i)
    {
        if(!sourceRects[i].is_valid())
            continue;

        Myth::Math::Vector2i targetPos(positions[i]);
        Myth::Math::Vector2i sourcePos(sourceRects[i].UpperLeftCorner);
        // This needs to be signed as it may go negative.
        Myth::Math::Vector2i sourceSize(sourceRects[i].get_size());

        // clip these coordinates
        if(targetPos.X<0)
        {
            sourceSize.X += targetPos.X;
            if(sourceSize.X <= 0)
                continue;

            sourcePos.X -= targetPos.X;
            targetPos.X = 0;
        }

        if(targetPos.X + sourceSize.X > (int32_t)renderTargetSize.X)
        {
            sourceSize.X -= (targetPos.X + sourceSize.X) - renderTargetSize.X;
            if(sourceSize.X <= 0)
                continue;
        }

        if(targetPos.Y<0)
        {
            sourceSize.Y += targetPos.Y;
            if(sourceSize.Y <= 0)
                continue;

            sourcePos.Y -= targetPos.Y;
            targetPos.Y = 0;
        }

        if(targetPos.Y + sourceSize.Y > (int32_t)renderTargetSize.Y)
        {
            sourceSize.Y -= (targetPos.Y + sourceSize.Y) - renderTargetSize.Y;
            if(sourceSize.Y <= 0)
                continue;
        }

        // ok, we've clipped everything.
        // now draw it.

        const Myth::Math::Rectanglef tcoords(
            sourcePos.X * invW,
            (isRTT?(sourcePos.Y + sourceSize.Y):sourcePos.Y) * invH,
            (sourcePos.X + sourceSize.X) * invW,
            (isRTT?sourcePos.Y:(sourcePos.Y + sourceSize.Y)) * invH);

        const Myth::Math::Rectanglei poss(targetPos, sourceSize+targetPos);

        glTexCoord2f(tcoords.UpperLeftCorner.X, tcoords.UpperLeftCorner.Y);
        glVertex2f(GLfloat(poss.UpperLeftCorner.X), GLfloat(poss.UpperLeftCorner.Y));

        glTexCoord2f(tcoords.LowerRightCorner.X, tcoords.UpperLeftCorner.Y);
        glVertex2f(GLfloat(poss.LowerRightCorner.X), GLfloat(poss.UpperLeftCorner.Y));

        glTexCoord2f(tcoords.LowerRightCorner.X, tcoords.LowerRightCorner.Y);
        glVertex2f(GLfloat(poss.LowerRightCorner.X), GLfloat(poss.LowerRightCorner.Y));

        glTexCoord2f(tcoords.UpperLeftCorner.X, tcoords.LowerRightCorner.Y);
        glVertex2f(GLfloat(poss.UpperLeftCorner.X), GLfloat(poss.LowerRightCorner.Y));
    }
    glEnd();
}

//! Draws a part of the texture into the rectangle.
void VideoManager_C::draw_2d_image(const Texture* texture, const Myth::Math::Rectanglei destRect,
                                 const Myth::Math::Rectanglei sourceRect,
                                 const Color* const colors, bool useAlphaChannelOfTexture)
{
    if(!texture)
        return;

    // texcoords need to be flipped horizontally for RTTs
    const bool isRTT = texture->is_render_target();
    const Myth::Math::Vector2u ss = texture->get_original_size();
    const float invW = 1.f / static_cast<float>(ss.X);
    const float invH = 1.f / static_cast<float>(ss.Y);
    const Myth::Math::Rectanglef tcoords(
        sourceRect.UpperLeftCorner.X * invW,
        (isRTT?sourceRect.LowerRightCorner.Y:sourceRect.UpperLeftCorner.Y) * invH,
        sourceRect.LowerRightCorner.X * invW,
        (isRTT?sourceRect.UpperLeftCorner.Y:sourceRect.LowerRightCorner.Y) *invH);

    const Color temp[4] =
    {
        0xFFFFFFFF,
        0xFFFFFFFF,
        0xFFFFFFFF,
        0xFFFFFFFF
    };

    const Color* const useColor = colors ? colors : temp;

    disable_textures(1);
    set_active_texture(0, texture);
    set_render_states_2d_mode(useColor[0].get_alpha()<255 || useColor[1].get_alpha()<255 ||
                          useColor[2].get_alpha()<255 || useColor[3].get_alpha()<255,
                          true, useAlphaChannelOfTexture);

    glBegin(GL_QUADS);

    glColor4ub(useColor[0].get_red(), useColor[0].get_green(), useColor[0].get_blue(), useColor[0].get_alpha());
    glTexCoord2f(tcoords.UpperLeftCorner.X, tcoords.UpperLeftCorner.Y);
    glVertex2f(GLfloat(destRect.UpperLeftCorner.X), GLfloat(destRect.UpperLeftCorner.Y));

    glColor4ub(useColor[3].get_red(), useColor[3].get_green(), useColor[3].get_blue(), useColor[3].get_alpha());
    glTexCoord2f(tcoords.LowerRightCorner.X, tcoords.UpperLeftCorner.Y);
    glVertex2f(GLfloat(destRect.LowerRightCorner.X), GLfloat(destRect.UpperLeftCorner.Y));

    glColor4ub(useColor[2].get_red(), useColor[2].get_green(), useColor[2].get_blue(), useColor[2].get_alpha());
    glTexCoord2f(tcoords.LowerRightCorner.X, tcoords.LowerRightCorner.Y);
    glVertex2f(GLfloat(destRect.LowerRightCorner.X), GLfloat(destRect.LowerRightCorner.Y));

    glColor4ub(useColor[1].get_red(), useColor[1].get_green(), useColor[1].get_blue(), useColor[1].get_alpha());
    glTexCoord2f(tcoords.UpperLeftCorner.X, tcoords.LowerRightCorner.Y);
    glVertex2f(GLfloat(destRect.UpperLeftCorner.X), GLfloat(destRect.LowerRightCorner.Y));

    glEnd();
}


void VideoManager_C::set_clip_rectangle(Myth::Math::Rectanglei *clip)
{
    if(clip)
    {
        if(!clip->is_valid() || *clip == Myth::Math::Rectanglei(0,0,0,0))
            return;

        glEnable(GL_SCISSOR_TEST);
        const Myth::Math::Vector2u renderTargetSize = get_current_render_target_size();
        glScissor(clip->UpperLeftCorner.X, renderTargetSize.Y - clip->LowerRightCorner.Y, clip->get_width(), clip->get_height());
        mClipRectangle = *clip;
    }
    else
    {
        if(get_clip_rectangle())
        {
            glDisable(GL_SCISSOR_TEST);
            mClipRectangle = Myth::Math::Rectanglei(0,0,0,0);
        }
    }
}

const Myth::Math::Rectanglei* VideoManager_C::get_clip_rectangle(void) const
{
    if(mClipRectangle == Myth::Math::Rectanglei(0,0,0,0))
        return 0;
    else
        return &mClipRectangle;
}

//! Draws a 2d image, using a color (if color is other then Color(255,255,255,255)) and the alpha channel of the texture if wanted.
void VideoManager_C::draw_2d_image(const Texture* texture, const Myth::Math::Vector2i destPos,
                                 const Myth::Math::Rectanglei sourceRect, Color color,
                                 bool useAlphaChannelOfTexture)
{
    if(!texture)
        return;

    if(!sourceRect.is_valid())
        return;

    Myth::Math::Vector2i targetPos(destPos);
    Myth::Math::Vector2i sourcePos(sourceRect.UpperLeftCorner);
    // This needs to be signed as it may go negative.
    Myth::Math::Vector2i sourceSize(sourceRect.get_size());

    // clip these coordinates

    if(targetPos.X<0)
    {
        sourceSize.X += targetPos.X;
        if(sourceSize.X <= 0)
            return;

        sourcePos.X -= targetPos.X;
        targetPos.X = 0;
    }

    const Myth::Math::Vector2u renderTargetSize = get_current_render_target_size();

    if(targetPos.X + sourceSize.X > (int32_t)renderTargetSize.X)
    {
        sourceSize.X -= (targetPos.X + sourceSize.X) - renderTargetSize.X;
        if(sourceSize.X <= 0)
            return;
    }

    if(targetPos.Y<0)
    {
        sourceSize.Y += targetPos.Y;
        if(sourceSize.Y <= 0)
            return;

        sourcePos.Y -= targetPos.Y;
        targetPos.Y = 0;
    }

    if(targetPos.Y + sourceSize.Y > (int32_t)renderTargetSize.Y)
    {
        sourceSize.Y -= (targetPos.Y + sourceSize.Y) - renderTargetSize.Y;
        if(sourceSize.Y <= 0)
            return;
    }

    // ok, we've clipped everything.
    // now draw it.

    // texcoords need to be flipped horizontally for RTTs
    const bool isRTT = texture->is_render_target();
    const Myth::Math::Vector2u ss = texture->get_original_size();
    const float invW = 1.f / static_cast<float>(ss.X);
    const float invH = 1.f / static_cast<float>(ss.Y);
    const Myth::Math::Rectanglef tcoords(
        sourcePos.X * invW,
        (isRTT?(sourcePos.Y + sourceSize.Y):sourcePos.Y) * invH,
        (sourcePos.X + sourceSize.X) * invW,
        (isRTT?sourcePos.Y:(sourcePos.Y + sourceSize.Y)) * invH);

    const Myth::Math::Rectanglei poss(targetPos, targetPos+sourceSize);

    disable_textures(1);
    if(!set_active_texture(0, texture))
        return;
    set_render_states_2d_mode(color.get_alpha()<255, true, useAlphaChannelOfTexture);

    glColor4ub(color.get_red(), color.get_green(), color.get_blue(), color.get_alpha());
    glBegin(GL_QUADS);

    glTexCoord2f(tcoords.UpperLeftCorner.X, tcoords.UpperLeftCorner.Y);
    glVertex2f(GLfloat(poss.UpperLeftCorner.X), GLfloat(poss.UpperLeftCorner.Y));

    glTexCoord2f(tcoords.LowerRightCorner.X, tcoords.UpperLeftCorner.Y);
    glVertex2f(GLfloat(poss.LowerRightCorner.X), GLfloat(poss.UpperLeftCorner.Y));

    glTexCoord2f(tcoords.LowerRightCorner.X, tcoords.LowerRightCorner.Y);
    glVertex2f(GLfloat(poss.LowerRightCorner.X), GLfloat(poss.LowerRightCorner.Y));

    glTexCoord2f(tcoords.UpperLeftCorner.X, tcoords.LowerRightCorner.Y);
    glVertex2f(GLfloat(poss.UpperLeftCorner.X), GLfloat(poss.LowerRightCorner.Y));

    glEnd();
}

//! Draws the outline of a 2d rectangle
void VideoManager_C::draw_2d_rectangle_outline(const Myth::Math::Rectanglei pos, Color color)
{
    draw_2d_line(pos.UpperLeftCorner, Myth::Math::Vector2i(pos.LowerRightCorner.X, pos.UpperLeftCorner.Y), color);
    draw_2d_line(Myth::Math::Vector2i(pos.LowerRightCorner.X, pos.UpperLeftCorner.Y), pos.LowerRightCorner, color);
    draw_2d_line(pos.LowerRightCorner, Myth::Math::Vector2i(pos.UpperLeftCorner.X, pos.LowerRightCorner.Y), color);
    draw_2d_line(Myth::Math::Vector2i(pos.UpperLeftCorner.X, pos.LowerRightCorner.Y), pos.UpperLeftCorner, color);
}

//! Draw a 2d rectangle
void VideoManager_C::draw_2d_rectangle(Color color, const Myth::Math::Rectanglei position)
{
    disable_textures();
    set_render_states_2d_mode(color.get_alpha() < 255, false, false);

    Myth::Math::Rectanglei pos = position;

    if(!pos.is_valid())
        return;

    glColor4ub(color.get_red(), color.get_green(), color.get_blue(), color.get_alpha());
    glRectf(GLfloat(pos.UpperLeftCorner.X), GLfloat(pos.UpperLeftCorner.Y),
            GLfloat(pos.LowerRightCorner.X), GLfloat(pos.LowerRightCorner.Y));
}

//! Draws a 2d rectangle with a gradient.
void VideoManager_C::draw_2d_rectangle(const Myth::Math::Rectanglei position,
                                     Color colorLeftUp, Color colorRightUp, Color colorLeftDown, Color colorRightDown)
{
    Myth::Math::Rectanglei pos = position;

    if(!pos.is_valid())
        return;

    disable_textures();

    set_render_states_2d_mode(colorLeftUp.get_alpha() < 255 ||
                          colorRightUp.get_alpha() < 255 ||
                          colorLeftDown.get_alpha() < 255 ||
                          colorRightDown.get_alpha() < 255, false, false);

    glBegin(GL_QUADS);
    glColor4ub(colorLeftUp.get_red(), colorLeftUp.get_green(),
               colorLeftUp.get_blue(), colorLeftUp.get_alpha());
    glVertex2f(GLfloat(pos.UpperLeftCorner.X), GLfloat(pos.UpperLeftCorner.Y));

    glColor4ub(colorRightUp.get_red(), colorRightUp.get_green(),
               colorRightUp.get_blue(), colorRightUp.get_alpha());
    glVertex2f(GLfloat(pos.LowerRightCorner.X), GLfloat(pos.UpperLeftCorner.Y));

    glColor4ub(colorRightDown.get_red(), colorRightDown.get_green(),
               colorRightDown.get_blue(), colorRightDown.get_alpha());
    glVertex2f(GLfloat(pos.LowerRightCorner.X), GLfloat(pos.LowerRightCorner.Y));

    glColor4ub(colorLeftDown.get_red(), colorLeftDown.get_green(),
               colorLeftDown.get_blue(), colorLeftDown.get_alpha());
    glVertex2f(GLfloat(pos.UpperLeftCorner.X), GLfloat(pos.LowerRightCorner.Y));

    glEnd();
}

//! Draws a 2d line.
void VideoManager_C::draw_2d_line(const Myth::Math::Vector2i start, const Myth::Math::Vector2i end, Color color)
{
    disable_textures();
    set_render_states_2d_mode(color.get_alpha() < 255, false, false);

    glBegin(GL_LINES);
    glColor4ub(color.get_red(), color.get_green(), color.get_blue(), color.get_alpha());
    glVertex2f(GLfloat(start.X), GLfloat(start.Y));
    glVertex2f(GLfloat(end.X),   GLfloat(end.Y));
    glEnd();
}

void VideoManager_C::draw_2d_lines(std::vector<Myth::Math::Line2i> mLines, Color color)
{
    disable_textures();
    set_render_states_2d_mode(color.get_alpha() < 255, false, false);

    glBegin(GL_LINES);
    glColor4ub(color.get_red(), color.get_green(), color.get_blue(), color.get_alpha());
    for(uint32_t a = 0; a < mLines.size(); a++)
    {
        glVertex2f(GLfloat(mLines[a].mStart.X), GLfloat(mLines[a].mStart.Y));
        glVertex2f(GLfloat(mLines[a].mEnd.X),   GLfloat(mLines[a].mEnd.Y));
    }
    glEnd();
}

//! Draws a pixel
void VideoManager_C::draw_pixel(uint32_t x, uint32_t y, const Color color)
{
    const Myth::Math::Vector2u renderTargetSize = get_current_render_target_size();
    if(x > (uint32_t)renderTargetSize.X || y > (uint32_t)renderTargetSize.Y)
        return;

    disable_textures();
    set_render_states_2d_mode(color.get_alpha() < 255, false, false);

    glBegin(GL_POINTS);
    glColor4ub(color.get_red(), color.get_green(), color.get_blue(), color.get_alpha());
    glVertex2i(x, y);
    glEnd();
}

//! Draws a non filled concyclic regular 2d polyon.
void VideoManager_C::draw_2d_polygon(Myth::Math::Vector2i center, float radius, Color color, int32_t count)
{
    if(count < 2)
        return;

    Vertex* vertices = new Vertex[count];

    uint16_t* indices = new uint16_t[count];
    for(uint16_t i = 0; i < count; i++)
    {
        indices[i] = i;
        vertices[i].mColor = color;
    }

    for(int32_t j=0; j<count; j++)
    {
        float p = j / (float)count * (Myth::Math::PI*2);
        vertices[j].mPos = Myth::Math::Vector3f(center.X + sin(p)*radius, center.Y + cos(p)*radius, 0.0f);
    }

    draw_2d_vertex_primitive_list(vertices, count, indices, count, EVT_STANDARD, EPT_LINE_LOOP, EIT_16BIT);
}

//! returns color format
E_COLOR_FORMAT VideoManager_C::get_color_format() const
{
    return mColorFormat;
}

//! returns screen size
const Myth::Math::Vector2u VideoManager_C::get_screen_size() const
{
    return mScreenSize;
}

///////////////////
void VideoManager_C::toggle_v_sync(bool on)
{
    #ifdef GLX_SGI_swap_control
    if(glxSwapIntervalSGI)
        glxSwapIntervalSGI(on ? 1 : 0);
    #endif
}
///////////////////

//! returns the current render target size,
//! or the screen size if render targets are not implemented
const Myth::Math::Vector2u VideoManager_C::get_current_render_target_size() const
{
    if(mCurrentRendertargetSize.X == 0)
        return mScreenSize;
    else
        return mCurrentRendertargetSize;
}

// returns current frames per second value
int32_t VideoManager_C::get_fps() const
{
    return mFPSCounter.get_fps();
}

//! returns amount of primitives (mostly triangles) were drawn in the last frame.
//! very useful method for statistics.
uint32_t VideoManager_C::get_primitive_count_drawn(uint32_t param) const
{
    return (0 == param) ? mFPSCounter.get_primitive() : (1 == param) ? mFPSCounter.get_primitive_average() : mFPSCounter.get_primitive_total();
}

//! Sets the dynamic ambient light color. The default color is
//! (0,0,0,0) which means it is dark.
//! \param color: New color of the ambient light.
void VideoManager_C::set_ambient_light(const Colorf color)
{
    GLfloat data[4] = {color.mR, color.mG, color.mB, color.mA};
    glLightModelfv(GL_LIGHT_MODEL_AMBIENT, data);
}

//! deletes all dynamic lights there are
void VideoManager_C::delete_all_dynamic_lights()
{
    for(int32_t i=0; i<mMaxLights; ++i)
        glDisable(GL_LIGHT0 + i);

    mRequestedLights.clear();

    mLights.reserve(0);
}

//! adds a dynamic light
int32_t VideoManager_C::add_dynamic_light(const Light light)
{
    mLights.push_back(light);

    mRequestedLights.push_back(RequestedLight(light));

    uint32_t newLightIndex = mRequestedLights.size() - 1;

    // Try and assign a hardware light just now, but don't worry if I can't
    assign_hardware_light(newLightIndex);

    return (int32_t)newLightIndex;
}

void VideoManager_C::assign_hardware_light(uint32_t lightIndex)
{
    set_transform(ETS_WORLD, Myth::Math::Matrix4());

    int32_t lidx;
    for(lidx=GL_LIGHT0; lidx < GL_LIGHT0 + mMaxLights; ++lidx)
    {
        if(!glIsEnabled(lidx))
        {
            mRequestedLights[lightIndex].mHardwareLightIndex = lidx;
            break;
        }
    }

    if(lidx == GL_LIGHT0 + mMaxLights) // There's no room for it just now
        return;

    GLfloat data[4];
    const Light& light = mRequestedLights[lightIndex].mLightData;

    switch(light.mType)
    {
    case ELT_SPOT:
        data[0] = light.mDirection.X;
        data[1] = light.mDirection.Y;
        data[2] = light.mDirection.Z;
        data[3] = 0.0f;
        glLightfv(lidx, GL_SPOT_DIRECTION, data);

        // set position
        data[0] = light.mPosition.X;
        data[1] = light.mPosition.Y;
        data[2] = light.mPosition.Z;
        data[3] = 1.0f; // 1.0f for positional light
        glLightfv(lidx, GL_POSITION, data);

        glLightf(lidx, GL_SPOT_EXPONENT, light.mFalloff);
        glLightf(lidx, GL_SPOT_CUTOFF, light.mOuterCone);
        break;
    case ELT_POINT:
        // set position
        data[0] = light.mPosition.X;
        data[1] = light.mPosition.Y;
        data[2] = light.mPosition.Z;
        data[3] = 1.0f; // 1.0f for positional light
        glLightfv(lidx, GL_POSITION, data);

        glLightf(lidx, GL_SPOT_EXPONENT, 0.0f);
        glLightf(lidx, GL_SPOT_CUTOFF, 180.0f);
        break;
    case ELT_DIRECTIONAL:
        // set direction
        data[0] = -light.mDirection.X;
        data[1] = -light.mDirection.Y;
        data[2] = -light.mDirection.Z;
        data[3] = 0.0f; // 0.0f for directional light
        glLightfv(lidx, GL_POSITION, data);

        glLightf(lidx, GL_SPOT_EXPONENT, 0.0f);
        glLightf(lidx, GL_SPOT_CUTOFF, 180.0f);
        break;
    }

    // set diffuse color
    data[0] = light.mDiffuseColor.mR;
    data[1] = light.mDiffuseColor.mG;
    data[2] = light.mDiffuseColor.mB;
    data[3] = light.mDiffuseColor.mA;
    glLightfv(lidx, GL_DIFFUSE, data);

    // set specular color
    data[0] = light.mSpecularColor.mR;
    data[1] = light.mSpecularColor.mG;
    data[2] = light.mSpecularColor.mB;
    data[3] = light.mSpecularColor.mA;
    glLightfv(lidx, GL_SPECULAR, data);

    // set ambient color
    data[0] = light.mAmbientColor.mR;
    data[1] = light.mAmbientColor.mG;
    data[2] = light.mAmbientColor.mB;
    data[3] = light.mAmbientColor.mA;
    glLightfv(lidx, GL_AMBIENT, data);

    // 1.0f / (constant + linear * d + quadratic*(d*d);

    // set attenuation
    glLightf(lidx, GL_CONSTANT_ATTENUATION, light.mAttenuation.X);
    glLightf(lidx, GL_LINEAR_ATTENUATION, light.mAttenuation.Y);
    glLightf(lidx, GL_QUADRATIC_ATTENUATION, light.mAttenuation.Z);

    glEnable(lidx);
}

//! Turns a dynamic light on or off
//! \param lightIndex: the index returned by add_dynamic_light
//! \param turnOn: true to turn the light on, false to turn it off
void VideoManager_C::turn_light_on(int32_t lightIndex, bool turnOn)
{
    if(lightIndex < 0 || lightIndex >= (int32_t)mRequestedLights.size())
        return;

    RequestedLight& requestedLight = mRequestedLights[lightIndex];

    requestedLight.mDesireToBeOn = turnOn;

    if(turnOn)
    {
        if(-1 == requestedLight.mHardwareLightIndex)
            assign_hardware_light(lightIndex);
    }
    else
    {
        if(-1 != requestedLight.mHardwareLightIndex)
        {
            // It's currently assigned, so free up the hardware light
            glDisable(requestedLight.mHardwareLightIndex);
            requestedLight.mHardwareLightIndex = -1;

            // Now let the first light that's waiting on a free hardware light increment_counter it
            for(uint32_t requested = 0; requested < mRequestedLights.size(); ++requested)
                if(mRequestedLights[requested].mDesireToBeOn
                        &&
                        -1 == mRequestedLights[requested].mHardwareLightIndex)
                {
                    assign_hardware_light(requested);
                    break;
                }
        }
    }
}

//! returns the maximal amount of dynamic lights the device can handle
uint32_t VideoManager_C::get_maximal_dynamic_light_amount() const
{
    return mMaxLights;
}

//! Returns current amount of dynamic lights set
//! \return Current amount of dynamic lights set
uint32_t VideoManager_C::get_dynamic_light_count() const
{
    return mLights.size();
}

//! Returns light data which was previously set by IVideoDriver::add_dynamic_light().
//! \param idx: Zero based index of the light. Must be greater than 0 and smaller
//! than IVideoDriver()::get_dynamic_light_count.
//! \return Light data.
const Light VideoManager_C::get_dynamic_light(uint32_t idx) const
{
    if(idx < mLights.size())
        return mLights[idx];
    else
        return *((Light*)0);
}

//! Returns the maximum amount of primitives (mostly vertices) which
//! the device is able to render with one draw_indexed_triangle_list
//! call.
uint32_t VideoManager_C::get_maximum_primitive_count() const
{
    return 0x7fffffff;
}

//! checks triangle count and print warning if wrong
bool VideoManager_C::check_primitive_count(uint32_t prmCount) const
{
    const uint32_t m = get_maximum_primitive_count();

    if(prmCount > m)
    {
        char tmp[1024];
        sprintf(tmp,"Could not draw triangles, too many primitives(%u), maxium is %u.", prmCount, m);
        mLogManager->log(tmp, ELL_ERROR);
        return false;
    }

    return true;
}

//! Enables or disables a texture creation flag.
void VideoManager_C::set_texture_creation_flag(E_TEXTURE_CREATION_FLAG flag, bool enabled)
{
    if(enabled && ((flag == ETCF_ALWAYS_16_BIT) || (flag == ETCF_ALWAYS_32_BIT)
                   || (flag == ETCF_OPTIMIZED_FOR_QUALITY) || (flag == ETCF_OPTIMIZED_FOR_SPEED)))
    {
        // disable other formats
        set_texture_creation_flag(ETCF_ALWAYS_16_BIT, false);
        set_texture_creation_flag(ETCF_ALWAYS_32_BIT, false);
        set_texture_creation_flag(ETCF_OPTIMIZED_FOR_QUALITY, false);
        set_texture_creation_flag(ETCF_OPTIMIZED_FOR_SPEED, false);
    }

    // set flag
    mTextureCreationFlags = (mTextureCreationFlags & (~flag)) |
                            ((((uint32_t)!enabled)-1) & flag);
}

//! Returns if a texture creation flag is enabled or disabled.
bool VideoManager_C::get_texture_creation_flag(E_TEXTURE_CREATION_FLAG flag) const
{
    return (mTextureCreationFlags & flag)!=0;
}

//! Creates a software image from part of a texture.
Image* VideoManager_C::create_image(Texture* texture, const Myth::Math::Vector2i pos, const Myth::Math::Vector2u size)
{
    if(pos==Myth::Math::Vector2i(0,0) && size == texture->get_size())
    {
        Image* image = new Image_C(texture->get_color_format(), size, texture->lock(true), false);
        texture->unlock();
        return image;
    }
    else
    {
        // make sure to avoid buffer overruns
        // make the vector a separate variable for g++ 3.x
        const Myth::Math::Vector2u leftUpper(Myth::Math::clamp(static_cast<uint32_t>(pos.X), 0u, texture->get_size().X),
                                             Myth::Math::clamp(static_cast<uint32_t>(pos.Y), 0u, texture->get_size().Y));
        const Myth::Math::Rectangleu clamped(leftUpper,
                                             Myth::Math::Vector2u(Myth::Math::clamp(static_cast<uint32_t>(size.X), 0u, texture->get_size().X),
                                                     Myth::Math::clamp(static_cast<uint32_t>(size.Y), 0u, texture->get_size().Y)));
        if(!clamped.is_valid())
            return 0;
        void* src = texture->lock(true);
        if(!src)
            return 0;
        Image* image = new Image_C(texture->get_color_format(), clamped.get_size());
        void* dst = image->get_pointer();
        for(uint32_t i=clamped.UpperLeftCorner.X; i<clamped.get_height(); ++i)
        {
            ColorConverter::convert_viaFormat(src, texture->get_color_format(), clamped.get_width(), dst, image->get_color_format());
        }
        texture->unlock();
        return image;
    }
}

//! Sets the fog mode.
void VideoManager_C::set_fog(Color color, E_FOG_TYPE fogType, float start, float end,
                            float density, bool pixelFog, bool rangeFog)
{
    mFogColor = color;
    mFogType = fogType;
    mFogStart = start;
    mFogEnd = end;
    mFogDensity = density;
    mPixelFog = pixelFog;
    mRangeFog = rangeFog;

    glFogf(GL_FOG_MODE, GLfloat((fogType==EFT_FOG_LINEAR)? GL_LINEAR : (fogType==EFT_FOG_EXP)?GL_EXP:GL_EXP2));

    if(mFeatureAvailable[LYNX_EXT_fog_coord])
        glFogi(GL_FOG_COORDINATE_SOURCE, GL_FRAGMENT_DEPTH);
#ifdef GL_NV_fog_distance
    if(mFeatureAvailable[LYNX_NV_fog_distance])
    {
        if(rangeFog)
            glFogi(GL_FOG_DISTANCE_MODE_NV, GL_EYE_RADIAL_NV);
        else
            glFogi(GL_FOG_DISTANCE_MODE_NV, GL_EYE_PLANE_ABSOLUTE_NV);
    }
#endif

    if(fogType==EFT_FOG_LINEAR)
    {
        glFogf(GL_FOG_START, start);
        glFogf(GL_FOG_END, end);
    }
    else
        glFogf(GL_FOG_DENSITY, density);

    if(pixelFog)
        glHint(GL_FOG_HINT, GL_NICEST);
    else
        glHint(GL_FOG_HINT, GL_FASTEST);

    Colorf c(color);
    GLfloat data[4] = {c.mR, c.mG, c.mB, c.mA};
    glFogfv(GL_FOG_COLOR, data);
}

//! Gets the fog mode.
void VideoManager_C::get_fog(Color& color, E_FOG_TYPE& fogType, float& start, float& end,
                            float& density, bool& pixelFog, bool& rangeFog)
{
    color = mFogColor;
    fogType = mFogType;
    start = mFogStart;
    end = mFogEnd;
    density = mFogDensity;
    pixelFog = mPixelFog;
    rangeFog = mRangeFog;
}

//! Draws a mesh buffer
void VideoManager_C::draw_mesh_buffer(const MeshBuffer* mb)
{
    if(!mb)
        return;

    //IVertexBuffer and IIndexBuffer later
    HWBufferLink* HWBuffer = get_buffer_link(mb);

    if(HWBuffer)
        draw_hardware_buffer(HWBuffer);
    else
        draw_vertex_primitive_list(mb->get_vertices(), mb->get_vertex_count(), mb->get_indices(), mb->get_index_count()/3, mb->get_vertex_type(), EPT_TRIANGLES, mb->get_index_type());
}

VideoManager_C::HWBufferLink* VideoManager_C::get_buffer_link(const MeshBuffer* mb)
{
    if(!mb || !is_hardware_buffer_recommended(mb))
        return 0;

    //search for hardware links
    std::map<const MeshBuffer*, HWBufferLink* >::iterator node = mHWBufferMap.find(mb);
    if(node != mHWBufferMap.end())
        return node->second;

    return create_hardware_buffer(mb); //no hardware links, and mesh wants one, create it
}

//! Update all hardware buffers, remove unused ones
void VideoManager_C::update_all_hardware_buffers()
{
    std::map<const MeshBuffer*, HWBufferLink*>::iterator Iterator = mHWBufferMap.begin();

    for(; Iterator != mHWBufferMap.end(); Iterator++)
    {
        HWBufferLink* Link = Iterator->second;

        Link->mLastUsed++;
        if(Link->mLastUsed>20000)
        {
            delete_hardware_buffer(Link);

            // todo: needs better fix
            Iterator = mHWBufferMap.begin();
        }
    }
}

void VideoManager_C::delete_hardware_buffer(HWBufferLink* _HWBuffer)
{
    if(!_HWBuffer)
        return;

    HWBufferLink_opengl* HWBuffer=(HWBufferLink_opengl*)_HWBuffer;
    if(HWBuffer->vbo_verticesID)
    {
        extGlDeleteBuffers(1, &HWBuffer->vbo_verticesID);
        HWBuffer->vbo_verticesID=0;
    }
    if(HWBuffer->vbo_indicesID)
    {
        extGlDeleteBuffers(1, &HWBuffer->vbo_indicesID);
        HWBuffer->vbo_indicesID=0;
    }

    mHWBufferMap.erase(HWBuffer->mMeshBuffer);
    delete HWBuffer;
}

//! Remove hardware buffer
void VideoManager_C::remove_hardware_buffer(const MeshBuffer* mb)
{
    std::map<const MeshBuffer*, HWBufferLink*>::iterator node = mHWBufferMap.find(mb);
    if(node != mHWBufferMap.end())
        delete_hardware_buffer(node->second);
}

//! Remove all hardware buffers
void VideoManager_C::remove_all_hardware_buffers()
{
    while(mHWBufferMap.size())
        delete_hardware_buffer(mHWBufferMap.begin()->second);
}

bool VideoManager_C::is_hardware_buffer_recommended(const MeshBuffer* mb)
{
    if(!mb || (mb->get_index_hardware_mapping_hint()== EHM_NEVER && mb->get_vertex_hardware_mapping_hint() == EHM_NEVER))
        return false;

    if(mb->get_vertex_count() < mMinVertexCountForVBO)
        return false;

    return true;
}

//! Only used by the internal engine. Used to notify the driver that
//! the window was resized.
void VideoManager_C::on_resize(const Myth::Math::Vector2u size)
{
    if(mViewPort.get_width() == (int32_t)mScreenSize.X &&
            mViewPort.get_height() == (int32_t)mScreenSize.Y)
        mViewPort = Myth::Math::Rectanglei(0, 0, size.X, size.Y);

    mScreenSize = size;

    glViewport(0, 0, size.X, size.Y);
    mTransformation3DChanged = true;
}

// adds a material renderer and drops it afterwards. To be used for internal creation
int32_t VideoManager_C::add_and_drop_material_renderer(MaterialRenderer* m)
{
    int32_t i = add_material_renderer(m);

    if(m)
        m->decrement_counter();

    return i;
}

//! Adds a new material renderer to the video device.
int32_t VideoManager_C::add_material_renderer(MaterialRenderer* renderer, const char* name)
{
    if(!renderer)
        return -1;

    SMaterialRenderer r;
    r.mRenderer = renderer;
    r.mName = name ? name : "";

    if(name == 0 && (mMaterialRenderers.size() < (sizeof(sBuiltInMaterialTypeNames) / sizeof(char*))-1))
    {
        // set name of built in renderer so that we don't have to implement name
        // setting in all available renderers.
        r.mName = sBuiltInMaterialTypeNames[mMaterialRenderers.size()];
    }

    mMaterialRenderers.push_back(r);
    renderer->increment_counter();

    return mMaterialRenderers.size()-1;
}

//! Sets the name of a material renderer.
void VideoManager_C::set_material_renderer_name(int32_t idx, const char* name)
{
    if(idx < int32_t(sizeof(sBuiltInMaterialTypeNames) / sizeof(char*))-1 ||
            idx >= (int32_t)mMaterialRenderers.size())
        return;

    mMaterialRenderers[idx].mName = name;
}

//! deletes all material renderers
void VideoManager_C::delete_material_renderers()
{
    // delete material renderers
    for(uint32_t i=0; i<mMaterialRenderers.size(); ++i)
        if(mMaterialRenderers[i].mRenderer)
            mMaterialRenderers[i].mRenderer->decrement_counter();

    mMaterialRenderers.clear();
}

//! Returns pointer to material renderer or null
MaterialRenderer* VideoManager_C::get_material_renderer(uint32_t idx)
{
    if(idx < mMaterialRenderers.size())
        return mMaterialRenderers[idx].mRenderer;
    else
        return 0;
}

//! Returns amount of currently available material renderers.
uint32_t VideoManager_C::get_material_renderer_count() const
{
    return mMaterialRenderers.size();
}

//! Returns name of the material renderer
const char* VideoManager_C::get_material_renderer_name(uint32_t idx) const
{
    if(idx < mMaterialRenderers.size())
        return mMaterialRenderers[idx].mName.c_str();

    return 0;
}

//! Returns pointer to the IGPUProgrammingServices interface.
GPUProgrammingServices* VideoManager_C::get_gpu_programming_services()
{
    return this;
}

int32_t VideoManager_C::add_high_level_shader_material(const char* vertexShaderProgram, const char* vertexShaderEntryPointName,
        E_VERTEX_SHADER_TYPE vsCompileTarget,
        const char* pixelShaderProgram,
        const char* pixelShaderEntryPointName,
        E_PIXEL_SHADER_TYPE psCompileTarget,
        const char* geometryShaderProgram,
        const char* geometryShaderEntryPointName,
        E_GEOMETRY_SHADER_TYPE gsCompileTarget,
        E_PRIMITIVE_TYPE inType,
        E_PRIMITIVE_TYPE outType,
        uint32_t verticesOut,
        ShaderConstantSetCallBack* callback,
        E_MATERIAL_TYPE baseMaterial,
        int32_t userData)
{
    int32_t nr = -1;

    GLSLMaterialRenderer* r = new GLSLMaterialRenderer(
        this, nr,
        vertexShaderProgram, vertexShaderEntryPointName, vsCompileTarget,
        pixelShaderProgram, pixelShaderEntryPointName, psCompileTarget,
        geometryShaderProgram, geometryShaderEntryPointName, gsCompileTarget,
        inType, outType, verticesOut,
        callback,get_material_renderer(baseMaterial), userData);
    r->decrement_counter();
    return nr;
}

//! Like IGPUProgrammingServices::add_shader_material(), but loads from files.
int32_t VideoManager_C::add_high_level_shader_material_from_files(
    Myth::Stream::Input* vertexShaderProgram,
    const char* vertexShaderEntryPointName,
    E_VERTEX_SHADER_TYPE vsCompileTarget,
    Myth::Stream::Input* pixelShaderProgram,
    const char* pixelShaderEntryPointName,
    E_PIXEL_SHADER_TYPE psCompileTarget,
    Myth::Stream::Input* geometryShaderProgram,
    const char* geometryShaderEntryPointName,
    E_GEOMETRY_SHADER_TYPE gsCompileTarget,
    E_PRIMITIVE_TYPE inType,
    E_PRIMITIVE_TYPE outType,
    uint32_t verticesOut,
    ShaderConstantSetCallBack* callback,
    E_MATERIAL_TYPE baseMaterial,
    int32_t userData)
{
    char* vs = 0;
    char* ps = 0;
    char* gs = 0;

    if(vertexShaderProgram)
    {
        const long size = vertexShaderProgram->get_size();
        if(size)
        {
            vs = new char[size+1];
            vertexShaderProgram->read(vs, size);
            vs[size] = 0;
        }
    }

    if(pixelShaderProgram)
    {
        const long size = pixelShaderProgram->get_size();
        if(size)
        {
            // if both handles are the same we must reset the file
            if(pixelShaderProgram==vertexShaderProgram)
                pixelShaderProgram->seek(0);
            ps = new char[size+1];
            pixelShaderProgram->read(ps, size);
            ps[size] = 0;
        }
    }

    if(geometryShaderProgram)
    {
        const long size = geometryShaderProgram->get_size();
        if(size)
        {
            // if both handles are the same we must reset the file
            if((geometryShaderProgram==vertexShaderProgram) ||
                    (geometryShaderProgram==pixelShaderProgram))
                geometryShaderProgram->seek(0);
            gs = new char[size+1];
            geometryShaderProgram->read(gs, size);
            gs[size] = 0;
        }
    }

    int32_t result = this->add_high_level_shader_material(
                         vs, vertexShaderEntryPointName, vsCompileTarget,
                         ps, pixelShaderEntryPointName, psCompileTarget,
                         gs, geometryShaderEntryPointName, gsCompileTarget,
                         inType, outType, verticesOut,
                         callback, baseMaterial, userData);

    delete [] vs;
    delete [] ps;
    delete [] gs;

    return result;

}

//! Adds a new ASM shader material renderer to the VideoDriver
int32_t VideoManager_C::add_shader_material(const char* vertexShaderProgram,
        const char* pixelShaderProgram,
        ShaderConstantSetCallBack* callback,
        E_MATERIAL_TYPE baseMaterial,
        int32_t userData)
{
    int32_t nr = -1;
    ShaderMaterialRenderer* r = new ShaderMaterialRenderer(
        this, nr, vertexShaderProgram, pixelShaderProgram,
        callback, get_material_renderer(baseMaterial), userData);

    r->decrement_counter();
    return nr;
}

//! Like IGPUProgrammingServices::add_shader_material(), but loads from files.
int32_t VideoManager_C::add_shader_material_from_files(Myth::Stream::Input* vertexShaderProgram,
        Myth::Stream::Input* pixelShaderProgram,
        ShaderConstantSetCallBack* callback,
        E_MATERIAL_TYPE baseMaterial,
        int32_t userData)
{
    char* vs = 0;
    char* ps = 0;

    if(vertexShaderProgram)
    {
        const long size = vertexShaderProgram->get_size();
        if(size)
        {
            vs = new char[size+1];
            vertexShaderProgram->read(vs, size);
            vs[size] = 0;
        }
    }

    if(pixelShaderProgram)
    {
        const long size = pixelShaderProgram->get_size();
        if(size)
        {
            ps = new char[size+1];
            pixelShaderProgram->read(ps, size);
            ps[size] = 0;
        }
    }

    int32_t result = add_shader_material(vs, ps, callback, baseMaterial, userData);

    delete [] vs;
    delete [] ps;

    return result;

}

//! Creates a render target texture.
Texture* VideoManager_C::add_render_target_texture(const Myth::Math::Vector2u size,
        const std::string name, const E_COLOR_FORMAT format)
{
    //disable mip-mapping
    bool generateMipLevels = get_texture_creation_flag(ETCF_CREATE_MIP_MAPS);
    set_texture_creation_flag(ETCF_CREATE_MIP_MAPS, false);

    Texture* rtt = 0;
    // if driver supports FrameBufferObjects, use them
    if(query_feature(EVMF_FRAMEBUFFER_OBJECT))
    {
        rtt = new TextureFBO_C(size, name, this, format);
        if(rtt)
        {
            bool success = false;
            add_texture(rtt);
            Texture* tex = create_depth_texture(rtt);
            if(tex)
            {
                success = static_cast<TextureFBODepth_C*>(tex)->attach(rtt);
                tex->decrement_counter();
            }
            rtt->decrement_counter();
            if(!success)
            {
                remove_texture(rtt);
                rtt=0;
            }
        }
    }
    else
    {
        // the simple texture is only possible for size <= screensize
        // we try to find an optimal size with the original constraints
        Myth::Math::Vector2u destSize(Myth::Math::min(size.X,mScreenSize.X), Myth::Math::min(size.Y,mScreenSize.Y));
        destSize = destSize.get_optimal_size((size==size.get_optimal_size()), false, false);
        rtt = add_texture(destSize, name, ECF_A8R8G8B8);
        if(rtt)
        {
            static_cast<Texture_C*>(rtt)->setIsRenderTarget(true);
        }
    }

    //restore mip-mapping
    set_texture_creation_flag(ETCF_CREATE_MIP_MAPS, generateMipLevels);

    return rtt;
}

//! Clears the ZBuffer.
void VideoManager_C::clear_z_buffer()
{
    clear_buffers(false, true, false, 0x0);
}

//! Returns a pointer to the mesh manipulator.
MeshManipulator* VideoManager_C::get_mesh_manipulator()
{
    return mMeshManipulator;
}

//! Returns an image created from the last rendered frame.
Image* VideoManager_C::create_screen_shot()
{
    Image* newImage = new Image_C(ECF_R8G8B8, mScreenSize);

    uint8_t* pixels = static_cast<uint8_t*>(newImage->get_pointer());
    if(!pixels)
    {
        newImage->decrement_counter();
        return 0;
    }

    // allows to read pixels in top-to-bottom order
#ifdef GL_MESA_pack_invert
    if(mFeatureAvailable[LYNX_MESA_pack_invert])
        glPixelStorei(GL_PACK_INVERT_MESA, GL_TRUE);
#endif

    // We want to read the front buffer to get the latest render finished.
    glReadBuffer(GL_FRONT);
    glReadPixels(0, 0, mScreenSize.X, mScreenSize.Y, GL_RGB, GL_UNSIGNED_BYTE, pixels);
    glReadBuffer(GL_BACK);

#ifdef GL_MESA_pack_invert
    if(mFeatureAvailable[LYNX_MESA_pack_invert])
        glPixelStorei(GL_PACK_INVERT_MESA, GL_FALSE);
    else
#endif
    {
        // opengl images are horizontally flipped, so we have to fix that here.
        const int32_t pitch=newImage->get_pitch();
        uint8_t* p2 = pixels + (mScreenSize.Y - 1) * pitch;
        uint8_t* tmpBuffer = new uint8_t[pitch];
        for(uint32_t i=0; i < mScreenSize.Y; i += 2)
        {
            memcpy(tmpBuffer, pixels, pitch);
            memcpy(pixels, p2, pitch);
            memcpy(p2, tmpBuffer, pitch);
            pixels += pitch;
            p2 -= pitch;
        }
        delete [] tmpBuffer;
    }

    return newImage;
}

//! get depth texture for the given render target texture
Texture* VideoManager_C::create_depth_texture(Texture* texture, bool shared)
{
    if(!texture->is_render_target())
        return 0;
    Texture_C* tex = static_cast<Texture_C*>(texture);

    if(!tex->isFrameBufferObject())
        return 0;

    if(shared)
    {
        for(uint32_t i=0; i<mDepthTextures.size(); ++i)
        {
            if(mDepthTextures[i]->get_size()==texture->get_size())
            {
                mDepthTextures[i]->increment_counter();
                return mDepthTextures[i];
            }
        }
        mDepthTextures.push_back(new TextureFBODepth_C(texture->get_size(), "depth1", this));
        return mDepthTextures.back();
    }
    return (new TextureFBODepth_C(texture->get_size(), "depth1", this));
}

void VideoManager_C::remove_depth_texture(Texture* texture)
{
    for(uint32_t i=0; i < mDepthTextures.size(); ++i)
    {
        if(texture == mDepthTextures[i])
        {
            mDepthTextures.erase(mDepthTextures.begin() + i);
            return;
        }
    }
}

//! Set/unset a clipping plane.
//! There are at least 6 clipping planes available for the user to set at will.
//! \param index: The plane index. Must be between 0 and MaxUserClipPlanes.
//! \param plane: The plane itself.
//! \param enable: If true, enable the clipping plane else disable it.
bool VideoManager_C::set_clip_plane(uint32_t index, const Myth::Math::Plane3f plane, bool enable)
{
    if(index >= mMaxUserClipPlanes)
        return false;

    mUserClipPlanes[index].mPlane = plane;
    enable_clip_plane(index, enable);
    return true;
}

//! Enable/disable a clipping plane.
void VideoManager_C::enable_clip_plane(uint32_t index, bool enable)
{
    if(index >= mMaxUserClipPlanes)
        return;
    if(enable)
    {
        if(!mUserClipPlanes[index].mEnabled)
        {
            upload_clip_plane(index);
            glEnable(GL_CLIP_PLANE0 + index);
        }
    }
    else
        glDisable(GL_CLIP_PLANE0 + index);

    mUserClipPlanes[index].mEnabled = enable;
}


void VideoManager_C::upload_clip_plane(uint32_t index)
{
    // opengl needs an array of doubles for the plane equation
    double clip_plane[4];
    clip_plane[0] = mUserClipPlanes[index].mPlane.mNormal.X;
    clip_plane[1] = mUserClipPlanes[index].mPlane.mNormal.Y;
    clip_plane[2] = mUserClipPlanes[index].mPlane.mNormal.Z;
    clip_plane[3] = mUserClipPlanes[index].mPlane.mD;
    glClipPlane(GL_CLIP_PLANE0 + index, clip_plane);
}

void VideoManager_C::set_min_hardware_buffer_vertex_count(uint32_t count)
{
    mMinVertexCountForVBO = count;
}

OverrideMaterial& VideoManager_C::get_override_material()
{
    return mOverrideMaterial;
}

//! Get the 2d override material for altering its values
Material& VideoManager_C::get_material_2d()
{
    return mOverrideMaterial2D;
}

//! Enable the 2d override material
void VideoManager_C::enable_material_2d(bool enable)
{
    if(!enable)
        mCurrentRenderMode = ERM_NONE;

    mOverrideMaterial2DEnabled = enable;
}

Myth::Math::Vector2u VideoManager_C::get_max_texture_size() const
{
    return Myth::Math::Vector2u(mMaxTextureSize, mMaxTextureSize); // maybe large enough
}

//! Can be called by an IMaterialRenderer to make its work easier.
void VideoManager_C::set_basic_render_states(const Material& material, const Material& lastmaterial, bool resetAllRenderStates)
{
    if(resetAllRenderStates || lastmaterial.mColorMaterial != material.mColorMaterial)
    {
        switch(material.mColorMaterial)
        {
        case ECM_NONE:
            glDisable(GL_COLOR_MATERIAL);
            break;
        case ECM_DIFFUSE:
            glColorMaterial(GL_FRONT_AND_BACK, GL_DIFFUSE);
            break;
        case ECM_AMBIENT:
            glColorMaterial(GL_FRONT_AND_BACK, GL_AMBIENT);
            break;
        case ECM_EMISSIVE:
            glColorMaterial(GL_FRONT_AND_BACK, GL_EMISSION);
            break;
        case ECM_SPECULAR:
            glColorMaterial(GL_FRONT_AND_BACK, GL_SPECULAR);
            break;
        case ECM_DIFFUSE_AND_AMBIENT:
            glColorMaterial(GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE);
            break;
        }
        if(material.mColorMaterial != ECM_NONE)
            glEnable(GL_COLOR_MATERIAL);
    }

    if(resetAllRenderStates ||
            lastmaterial.mAmbientColor != material.mAmbientColor ||
            lastmaterial.mDiffuseColor != material.mDiffuseColor ||
            lastmaterial.mEmissiveColor != material.mEmissiveColor ||
            lastmaterial.mColorMaterial != material.mColorMaterial)
    {
        GLfloat color[4];

        const float inv = 1.0f / 255.0f;

        if((material.mColorMaterial != ECM_AMBIENT) &&
                (material.mColorMaterial != ECM_DIFFUSE_AND_AMBIENT))
        {
            color[0] = material.mAmbientColor.get_red() * inv;
            color[1] = material.mAmbientColor.get_green() * inv;
            color[2] = material.mAmbientColor.get_blue() * inv;
            color[3] = material.mAmbientColor.get_alpha() * inv;
            glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT, color);
        }

        if((material.mColorMaterial != ECM_DIFFUSE) &&
                (material.mColorMaterial != ECM_DIFFUSE_AND_AMBIENT))
        {
            color[0] = material.mDiffuseColor.get_red() * inv;
            color[1] = material.mDiffuseColor.get_green() * inv;
            color[2] = material.mDiffuseColor.get_blue() * inv;
            color[3] = material.mDiffuseColor.get_alpha() * inv;
            glMaterialfv(GL_FRONT_AND_BACK, GL_DIFFUSE, color);
        }

        if(material.mColorMaterial != ECM_EMISSIVE)
        {
            color[0] = material.mEmissiveColor.get_red() * inv;
            color[1] = material.mEmissiveColor.get_green() * inv;
            color[2] = material.mEmissiveColor.get_blue() * inv;
            color[3] = material.mEmissiveColor.get_alpha() * inv;
            glMaterialfv(GL_FRONT_AND_BACK, GL_EMISSION, color);
        }
    }

    if(resetAllRenderStates ||
            lastmaterial.mSpecularColor != material.mSpecularColor ||
            lastmaterial.mShininess != material.mShininess ||
            lastmaterial.mColorMaterial != material.mColorMaterial)
    {
        GLfloat color[4]= {0.f,0.f,0.f,1.f};
        const float inv = 1.0f / 255.0f;

        glMaterialf(GL_FRONT_AND_BACK, GL_SHININESS, material.mShininess);
        // disable Specular colors if no shininess is set
        if((material.mShininess != 0.0f) &&
                (material.mColorMaterial != ECM_SPECULAR))
        {
            if(mFeatureAvailable[LYNX_EXT_separate_specular_color])
                glLightModeli(GL_LIGHT_MODEL_COLOR_CONTROL, GL_SEPARATE_SPECULAR_COLOR);
            color[0] = material.mSpecularColor.get_red() * inv;
            color[1] = material.mSpecularColor.get_green() * inv;
            color[2] = material.mSpecularColor.get_blue() * inv;
            color[3] = material.mSpecularColor.get_alpha() * inv;
        }
        else if(mFeatureAvailable[LYNX_EXT_separate_specular_color])
            glLightModeli(GL_LIGHT_MODEL_COLOR_CONTROL, GL_SINGLE_COLOR);
        glMaterialfv(GL_FRONT_AND_BACK, GL_SPECULAR, color);
    }

    // Texture filter
    // Has to be checked always because it depends on the textures
    // Filtering has to be set for each texture layer
    for(uint32_t i=0; i<mMaxTextureUnits; ++i)
    {
        if(mMultiTextureExtension)
            extGlActiveTexture(GL_TEXTURE0_ARB + i);
        else if(i>0)
            break;

        if(mFeatureAvailable[LYNX_EXT_texture_lod_bias])
        {
            if(material.mTextureLayer[i].mLODBias)
            {
                const float tmp = Myth::Math::clamp(material.mTextureLayer[i].mLODBias * 0.125f, -mMaxTextureLODBias, mMaxTextureLODBias);
                glTexEnvf(GL_TEXTURE_FILTER_CONTROL_EXT, GL_TEXTURE_LOD_BIAS_EXT, tmp);
            }
            else
                glTexEnvf(GL_TEXTURE_FILTER_CONTROL_EXT, GL_TEXTURE_LOD_BIAS_EXT, 0.f);
        }

        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER,
                        (material.mTextureLayer[i].mBilinearFilter || material.mTextureLayer[i].mTrilinearFilter) ? GL_LINEAR : GL_NEAREST);

        if(material.get_texture(i) && material.get_texture(i)->has_mip_maps())
            glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER,
                            material.mTextureLayer[i].mTrilinearFilter ? GL_LINEAR_MIPMAP_LINEAR :
                            material.mTextureLayer[i].mBilinearFilter ? GL_LINEAR_MIPMAP_NEAREST :
                            GL_NEAREST_MIPMAP_NEAREST);
        else
            glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER,
                            (material.mTextureLayer[i].mBilinearFilter || material.mTextureLayer[i].mTrilinearFilter) ? GL_LINEAR : GL_NEAREST);

        if(mFeatureAvailable[LYNX_EXT_texture_filter_anisotropic])
            glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT,
                            material.mTextureLayer[i].mAnisotropicFilter>1 ? Myth::Math::min(mMaxAnisotropy, material.mTextureLayer[i].mAnisotropicFilter) : 1);
    }

    // fillmode
    if(resetAllRenderStates || (lastmaterial.mWireframe != material.mWireframe) || (lastmaterial.mPointCloud != material.mPointCloud))
        glPolygonMode(GL_FRONT_AND_BACK, material.mWireframe ? GL_LINE : material.mPointCloud? GL_POINT : GL_FILL);

    // shademode
    if(resetAllRenderStates || (lastmaterial.mGouraudShading != material.mGouraudShading))
    {
        if(material.mGouraudShading)
            glShadeModel(GL_SMOOTH);
        else
            glShadeModel(GL_FLAT);
    }

    // lighting
    if(resetAllRenderStates || (lastmaterial.mLighting != material.mLighting))
    {
        if(material.mLighting)
            glEnable(GL_LIGHTING);
        else
            glDisable(GL_LIGHTING);
    }

    // zbuffer
    if(resetAllRenderStates || lastmaterial.mZBuffer != material.mZBuffer)
    {
        switch(material.mZBuffer)
        {
        case ECF_NEVER:
            glDisable(GL_DEPTH_TEST);
            break;
        case ECF_LESSEQUAL:
            glEnable(GL_DEPTH_TEST);
            glDepthFunc(GL_LEQUAL);
            break;
        case ECF_EQUAL:
            glEnable(GL_DEPTH_TEST);
            glDepthFunc(GL_EQUAL);
            break;
        case ECF_LESS:
            glEnable(GL_DEPTH_TEST);
            glDepthFunc(GL_LESS);
            break;
        case ECF_NOTEQUAL:
            glEnable(GL_DEPTH_TEST);
            glDepthFunc(GL_NOTEQUAL);
            break;
        case ECF_GREATEREQUAL:
            glEnable(GL_DEPTH_TEST);
            glDepthFunc(GL_GEQUAL);
            break;
        case ECF_GREATER:
            glEnable(GL_DEPTH_TEST);
            glDepthFunc(GL_GREATER);
            break;
        case ECF_ALWAYS:
            glEnable(GL_DEPTH_TEST);
            glDepthFunc(GL_ALWAYS);
            break;
        }
    }

    // zwrite
//	if (resetAllRenderStates || lastmaterial.ZWriteEnable != material.ZWriteEnable)
    {
        if(material.mZWriteEnable && (mAllowZWriteOnTransparent || !material.is_transparent()))
        {
            glDepthMask(GL_TRUE);
        }
        else
            glDepthMask(GL_FALSE);
    }

    // back face culling
    if(resetAllRenderStates || (lastmaterial.mFrontfaceCulling != material.mFrontfaceCulling) || (lastmaterial.mBackfaceCulling != material.mBackfaceCulling))
    {
        if((material.mFrontfaceCulling) && (material.mBackfaceCulling))
        {
            glCullFace(GL_FRONT_AND_BACK);
            glEnable(GL_CULL_FACE);
        }
        else if(material.mBackfaceCulling)
        {
            glCullFace(GL_BACK);
            glEnable(GL_CULL_FACE);
        }
        else if(material.mFrontfaceCulling)
        {
            glCullFace(GL_FRONT);
            glEnable(GL_CULL_FACE);
        }
        else
            glDisable(GL_CULL_FACE);
    }

    // fog
    if(resetAllRenderStates || lastmaterial.mFogEnable != material.mFogEnable)
    {
        if(material.mFogEnable)
            glEnable(GL_FOG);
        else
            glDisable(GL_FOG);
    }

    // normalization
    if(resetAllRenderStates || lastmaterial.mNormalizeNormals != material.mNormalizeNormals)
    {
        if(material.mNormalizeNormals)
            glEnable(GL_NORMALIZE);
        else
            glDisable(GL_NORMALIZE);
    }

    // Color Mask
    if(resetAllRenderStates || lastmaterial.mColorMask != material.mColorMask)
    {
        glColorMask(
            (material.mColorMask & ECP_RED)?GL_TRUE:GL_FALSE,
            (material.mColorMask & ECP_GREEN)?GL_TRUE:GL_FALSE,
            (material.mColorMask & ECP_BLUE)?GL_TRUE:GL_FALSE,
            (material.mColorMask & ECP_ALPHA)?GL_TRUE:GL_FALSE);
    }

    // thickness
    if(resetAllRenderStates || lastmaterial.mThickness != material.mThickness)
    {
        if(mAntiAlias)
        {
//			glPointSize(Myth::Math::clamp(static_cast<GLfloat>(material.Thickness), DimSmoothedPoint[0], DimSmoothedPoint[1]));
            // we don't use point smoothing
            glPointSize(Myth::Math::clamp(static_cast<GLfloat>(material.mThickness), mDimAliasedPoint[0], mDimAliasedPoint[1]));
            glLineWidth(Myth::Math::clamp(static_cast<GLfloat>(material.mThickness), mDimSmoothedLine[0], mDimSmoothedLine[1]));
        }
        else
        {
            glPointSize(Myth::Math::clamp(static_cast<GLfloat>(material.mThickness), mDimAliasedPoint[0], mDimAliasedPoint[1]));
            glLineWidth(Myth::Math::clamp(static_cast<GLfloat>(material.mThickness), mDimAliasedLine[0], mDimAliasedLine[1]));
        }
    }

    // Anti aliasing
    if(resetAllRenderStates || lastmaterial.mAntiAliasing != material.mAntiAliasing)
    {
        if(mFeatureAvailable[LYNX_ARB_multisample])
        {
            if(material.mAntiAliasing & EAAM_ALPHA_TO_COVERAGE)
                glEnable(GL_SAMPLE_ALPHA_TO_COVERAGE_ARB);
            else if(lastmaterial.mAntiAliasing & EAAM_ALPHA_TO_COVERAGE)
                glDisable(GL_SAMPLE_ALPHA_TO_COVERAGE_ARB);

            if((mAntiAlias >= 2) && (material.mAntiAliasing & (EAAM_SIMPLE|EAAM_QUALITY)))
            {
                glEnable(GL_MULTISAMPLE_ARB);
#ifdef GL_NV_multisample_filter_hint
				if (mFeatureAvailable[LYNX_NV_multisample_filter_hint])
				{
					if ((material.mAntiAliasing & EAAM_QUALITY) == EAAM_QUALITY)
						glHint(GL_MULTISAMPLE_FILTER_HINT_NV, GL_NICEST);
					else
						glHint(GL_MULTISAMPLE_FILTER_HINT_NV, GL_NICEST);
				}
#endif
            }
            else
                glDisable(GL_MULTISAMPLE_ARB);
        }
        if((material.mAntiAliasing & EAAM_LINE_SMOOTH) != (lastmaterial.mAntiAliasing & EAAM_LINE_SMOOTH))
        {
            if(material.mAntiAliasing & EAAM_LINE_SMOOTH)
                glEnable(GL_LINE_SMOOTH);
            else if(lastmaterial.mAntiAliasing & EAAM_LINE_SMOOTH)
                glDisable(GL_LINE_SMOOTH);
        }
        if((material.mAntiAliasing & EAAM_POINT_SMOOTH) != (lastmaterial.mAntiAliasing & EAAM_POINT_SMOOTH))
        {
            if(material.mAntiAliasing & EAAM_POINT_SMOOTH)
                // often in software, and thus very slow
                glEnable(GL_POINT_SMOOTH);
            else if(lastmaterial.mAntiAliasing & EAAM_POINT_SMOOTH)
                glDisable(GL_POINT_SMOOTH);
        }
    }

    set_wrap_mode(material);

    // be sure to leave in texture stage 0
    if(mMultiTextureExtension)
        extGlActiveTexture(GL_TEXTURE0_ARB);
}

void VideoManager_C::set_wrap_mode(const Material& material)
{
    // texture address mode
    // Has to be checked always because it depends on the textures
    for(uint32_t u=0; u<mMaxTextureUnits; ++u)
    {
        if(mMultiTextureExtension)
            extGlActiveTexture(GL_TEXTURE0_ARB + u);
        else if(u>0)
            break; // stop loop

        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, get_texture_wrap_mode(material.mTextureLayer[u].mTextureWrapU));
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, get_texture_wrap_mode(material.mTextureLayer[u].mTextureWrapV));
    }
}


//! Get native wrap mode value
GLint VideoManager_C::get_texture_wrap_mode(const uint8_t clamp)
{
    GLint mode=GL_REPEAT;

    switch(clamp)
    {
    case ETC_REPEAT:
        mode=GL_REPEAT;
        break;

    case ETC_CLAMP:
        mode=GL_CLAMP;
        break;

    case ETC_CLAMP_TO_EDGE:
        if(mVersion>101)
            mode=GL_CLAMP_TO_EDGE;
        else
            mode=GL_CLAMP;
        break;

    case ETC_CLAMP_TO_BORDER:
        if(mVersion>102)
            mode=GL_CLAMP_TO_BORDER;
        else
            mode=GL_CLAMP;
        break;

    case ETC_MIRROR:
        if(mVersion>103)
            mode=GL_MIRRORED_REPEAT;
        else
            mode=GL_REPEAT;
        break;

    case ETC_MIRROR_CLAMP:
        if(mFeatureAvailable[LYNX_EXT_texture_mirror_clamp])
            mode=GL_MIRROR_CLAMP_EXT;
        else
            mode=GL_CLAMP;
        break;

    case ETC_MIRROR_CLAMP_TO_EDGE:
        if(mFeatureAvailable[LYNX_EXT_texture_mirror_clamp])
            mode=GL_MIRROR_CLAMP_TO_EDGE_EXT;
        else
            mode=GL_CLAMP;
        break;

    case ETC_MIRROR_CLAMP_TO_BORDER:
        if(mFeatureAvailable[LYNX_EXT_texture_mirror_clamp])
            mode=GL_MIRROR_CLAMP_TO_BORDER_EXT;
        else
            mode=GL_CLAMP;
        break;
    }
    return mode;
}

//! Sets a vertex shader constant.
void VideoManager_C::set_vertex_shader_constant(const float* data, int32_t startRegister, int32_t constantAmount)
{
    for(int32_t i=0; i<constantAmount; ++i)
        extGlProgramLocalParameter4fv(GL_VERTEX_PROGRAM_ARB, startRegister+i, &data[i*4]);
}

//! Sets a pixel shader constant.
void VideoManager_C::set_pixel_shader_constant(const float* data, int32_t startRegister, int32_t constantAmount)
{
    for(int32_t i=0; i<constantAmount; ++i)
        extGlProgramLocalParameter4fv(GL_FRAGMENT_PROGRAM_ARB, startRegister+i, &data[i*4]);
}

//! Sets a constant for the vertex shader based on a name.
bool VideoManager_C::set_vertex_shader_constant(const char* name, const float* floats, int count)
{
    //pass this along, as in GLSL the same routine is used for both vertex and fragment shaders
    return set_pixel_shader_constant(name, floats, count);
}

//! Sets a constant for the pixel shader based on a name.
bool VideoManager_C::set_pixel_shader_constant(const char* name, const float* floats, int count)
{
    mLogManager->log("Error: Please call services->set_pixel_shader_constant(), not VideoDriver->set_pixel_shader_constant().");
    return false;
}


bool VideoManager_C::update_vertex_hardware_buffer(HWBufferLink_opengl* HWBuffer)
{
    if(!HWBuffer)
        return false;

    if(!mFeatureAvailable[LYNX_ARB_vertex_buffer_object])
        return false;

    const MeshBuffer* mb = HWBuffer->mMeshBuffer;
    const void* vertices=mb->get_vertices();
    const uint32_t vertexCount=mb->get_vertex_count();
    const E_VERTEX_TYPE vType=mb->get_vertex_type();
    const uint32_t vertexSize = get_vertex_pitch_from_type(vType);

    //buffer vertex data, and convert colours...
    std::vector<char> buffer(vertexSize * vertexCount);
    memcpy(&buffer[0], vertices, vertexSize * vertexCount);

    // in order to convert the colors into opengl format (RGBA)
    switch(vType)
    {
    case EVT_STANDARD:
    {
        Vertex* pb = reinterpret_cast<Vertex*>(&buffer[0]);
        const Vertex* po = static_cast<const Vertex*>(vertices);
        for(uint32_t i=0; i<vertexCount; i++)
        {
            po[i].mColor.to_opengl_color((uint8_t*)&(pb[i].mColor));
        }
    }
    break;
    case EVT_2TCOORDS:
    {
        Vertex2TCoords* pb = reinterpret_cast<Vertex2TCoords*>(&buffer[0]);
        const Vertex2TCoords* po = static_cast<const Vertex2TCoords*>(vertices);
        for(uint32_t i=0; i<vertexCount; i++)
        {
            po[i].mColor.to_opengl_color((uint8_t*)&(pb[i].mColor));
        }
    }
    break;
    case EVT_TANGENTS:
    {
        VertexTangents* pb = reinterpret_cast<VertexTangents*>(&buffer[0]);
        const VertexTangents* po = static_cast<const VertexTangents*>(vertices);
        for(uint32_t i=0; i<vertexCount; i++)
        {
            po[i].mColor.to_opengl_color((uint8_t*)&(pb[i].mColor));
        }
    }
    break;
    default:
    {
        return false;
    }
    }

    //get or create buffer
    bool newBuffer=false;
    if(!HWBuffer->vbo_verticesID)
    {
        extGlGenBuffers(1, &HWBuffer->vbo_verticesID);
        if(!HWBuffer->vbo_verticesID)
            return false;
        newBuffer=true;
    }
    else if(HWBuffer->vbo_verticesSize < vertexCount*vertexSize)
    {
        newBuffer=true;
    }

    extGlBindBuffer(GL_ARRAY_BUFFER, HWBuffer->vbo_verticesID);

    //copy data to graphics card
    glGetError(); // clear error storage
    if(!newBuffer)
        extGlBufferSubData(GL_ARRAY_BUFFER, 0, vertexCount * vertexSize, &buffer[0]);
    else
    {
        HWBuffer->vbo_verticesSize = vertexCount*vertexSize;

        if(HWBuffer->mMapped_Vertex==EHM_STATIC)
            extGlBufferData(GL_ARRAY_BUFFER, vertexCount * vertexSize, &buffer[0], GL_STATIC_DRAW);
        else if(HWBuffer->mMapped_Vertex==EHM_DYNAMIC)
            extGlBufferData(GL_ARRAY_BUFFER, vertexCount * vertexSize, &buffer[0], GL_DYNAMIC_DRAW);
        else //scene::EHM_STREAM
            extGlBufferData(GL_ARRAY_BUFFER, vertexCount * vertexSize, &buffer[0], GL_STREAM_DRAW);
    }

    extGlBindBuffer(GL_ARRAY_BUFFER, 0);

    return (glGetError() == GL_NO_ERROR);
}


bool VideoManager_C::update_index_hardware_buffer(HWBufferLink_opengl* HWBuffer)
{
    if(!HWBuffer)
        return false;

    if(!mFeatureAvailable[LYNX_ARB_vertex_buffer_object])
        return false;

    const MeshBuffer* mb = HWBuffer->mMeshBuffer;

    const void* indices=mb->get_indices();
    uint32_t indexCount= mb->get_index_count();

    GLenum indexSize;
    switch(mb->get_index_type())
    {
    case EIT_16BIT:
    {
        indexSize=sizeof(uint16_t);
        break;
    }
    case EIT_32BIT:
    {
        indexSize=sizeof(uint32_t);
        break;
    }
    default:
    {
        return false;
    }
    }


    //get or create buffer
    bool newBuffer=false;
    if(!HWBuffer->vbo_indicesID)
    {
        extGlGenBuffers(1, &HWBuffer->vbo_indicesID);
        if(!HWBuffer->vbo_indicesID)
            return false;
        newBuffer=true;
    }
    else if(HWBuffer->vbo_indicesSize < indexCount*indexSize)
    {
        newBuffer=true;
    }

    extGlBindBuffer(GL_ELEMENT_ARRAY_BUFFER, HWBuffer->vbo_indicesID);

    //copy data to graphics card
    glGetError(); // clear error storage
    if(!newBuffer)
        extGlBufferSubData(GL_ELEMENT_ARRAY_BUFFER, 0, indexCount * indexSize, indices);
    else
    {
        HWBuffer->vbo_indicesSize = indexCount*indexSize;

        if(HWBuffer->mMapped_Index==EHM_STATIC)
            extGlBufferData(GL_ELEMENT_ARRAY_BUFFER, indexCount * indexSize, indices, GL_STATIC_DRAW);
        else if(HWBuffer->mMapped_Index==EHM_DYNAMIC)
            extGlBufferData(GL_ELEMENT_ARRAY_BUFFER, indexCount * indexSize, indices, GL_DYNAMIC_DRAW);
        else //scene::EHM_STREAM
            extGlBufferData(GL_ELEMENT_ARRAY_BUFFER, indexCount * indexSize, indices, GL_STREAM_DRAW);
    }

    extGlBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);

    return (glGetError() == GL_NO_ERROR);
}


//! updates hardware buffer if needed
bool VideoManager_C::update_hardware_buffer(HWBufferLink* HWBuffer)
{
    if(!HWBuffer)
        return false;

    if(HWBuffer->mMapped_Vertex!=EHM_NEVER)
    {
        if(HWBuffer->mChangedID_Vertex != HWBuffer->mMeshBuffer->get_vertex_changed_id()
                || !((HWBufferLink_opengl*)HWBuffer)->vbo_verticesID)
        {

            HWBuffer->mChangedID_Vertex = HWBuffer->mMeshBuffer->get_vertex_changed_id();

            if(!update_vertex_hardware_buffer((HWBufferLink_opengl*)HWBuffer))
                return false;
        }
    }

    if(HWBuffer->mMapped_Index!=EHM_NEVER)
    {
        if(HWBuffer->mChangedID_Index != HWBuffer->mMeshBuffer->get_index_changed_id()
                || !((HWBufferLink_opengl*)HWBuffer)->vbo_indicesID)
        {

            HWBuffer->mChangedID_Index = HWBuffer->mMeshBuffer->get_index_changed_id();

            if(!update_index_hardware_buffer((HWBufferLink_opengl*)HWBuffer))
                return false;
        }
    }

    return true;
}


//! Create hardware buffer from meshbuffer
VideoManager_C::HWBufferLink* VideoManager_C::create_hardware_buffer(const MeshBuffer* mb)
{
    if(!mb || (mb->get_index_hardware_mapping_hint()==EHM_NEVER && mb->get_vertex_hardware_mapping_hint()==EHM_NEVER))
        return 0;

    HWBufferLink_opengl* HWBuffer=new HWBufferLink_opengl(mb);

    //add to map
    mHWBufferMap[HWBuffer->mMeshBuffer] = HWBuffer;

    HWBuffer->mChangedID_Vertex=HWBuffer->mMeshBuffer->get_vertex_changed_id();
    HWBuffer->mChangedID_Index=HWBuffer->mMeshBuffer->get_index_changed_id();
    HWBuffer->mMapped_Vertex=mb->get_vertex_hardware_mapping_hint();
    HWBuffer->mMapped_Index=mb->get_index_hardware_mapping_hint();
    HWBuffer->mLastUsed=0;
    HWBuffer->vbo_verticesID=0;
    HWBuffer->vbo_indicesID=0;
    HWBuffer->vbo_verticesSize=0;
    HWBuffer->vbo_indicesSize=0;

    if(!update_hardware_buffer(HWBuffer))
    {
        delete_hardware_buffer(HWBuffer);
        return 0;
    }

    return HWBuffer;
}

//! Draw hardware buffer
void VideoManager_C::draw_hardware_buffer(HWBufferLink* _HWBuffer)
{
    if(!_HWBuffer)
        return;

    update_hardware_buffer(_HWBuffer); //check if update is needed
    _HWBuffer->mLastUsed=0; //reset count

    HWBufferLink_opengl* HWBuffer=(HWBufferLink_opengl*)_HWBuffer;

    const MeshBuffer* mb = HWBuffer->mMeshBuffer;
    const void* vertices=mb->get_vertices();
    const void* indexList=mb->get_indices();

    if(HWBuffer->mMapped_Vertex!=EHM_NEVER)
    {
        extGlBindBuffer(GL_ARRAY_BUFFER, HWBuffer->vbo_verticesID);
        vertices=0;
    }

    if(HWBuffer->mMapped_Index!=EHM_NEVER)
    {
        extGlBindBuffer(GL_ELEMENT_ARRAY_BUFFER, HWBuffer->vbo_indicesID);
        indexList=0;
    }

    draw_vertex_primitive_list(vertices, mb->get_vertex_count(), indexList, mb->get_index_count()/3, mb->get_vertex_type(), EPT_TRIANGLES, mb->get_index_type());

    if(HWBuffer->mMapped_Vertex!=EHM_NEVER)
        extGlBindBuffer(GL_ARRAY_BUFFER, 0);
    if(HWBuffer->mMapped_Index!=EHM_NEVER)
        extGlBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
}

VideoManager* VideoManager_C::get_video_manager()
{
    return this;
}

LogManager* VideoManager_C::get_log_manager() const
{
    return mLogManager;
}

bool VideoManager_C::set_active_texture(uint32_t stage, const Texture* texture)
{
    if(stage >= mMaxTextureUnits)
        return false;

    if(mCurrentTexture[stage] == texture)
        return true;

    if(mMultiTextureExtension)
        extGlActiveTexture(GL_TEXTURE0_ARB + stage);

    mCurrentTexture[stage] = texture;

    if(!texture)
    {
        glDisable(GL_TEXTURE_2D);
        return true;
    }
    else
    {
        glEnable(GL_TEXTURE_2D);
        glBindTexture(GL_TEXTURE_2D, static_cast<const Texture_C*>(texture)->getOpenGLTextureName());
    }
    return true;
}

bool VideoManager_C::disable_textures(uint32_t fromStage)
{
    bool result=true;
    for(uint32_t i = fromStage; i < mMaxTextureUnits; ++i)
        result &= set_active_texture(i, 0);
    return result;
}

//! Convert E_PRIMITIVE_TYPE to OpenGL equivalent
GLenum VideoManager_C::primitive_type_to_gl(E_PRIMITIVE_TYPE type) const
{
    switch(type)
    {
    case EPT_POINTS:
        return GL_POINTS;
    case EPT_LINE_STRIP:
        return GL_LINE_STRIP;
    case EPT_LINE_LOOP:
        return GL_LINE_LOOP;
    case EPT_LINES:
        return GL_LINES;
    case EPT_TRIANGLE_STRIP:
        return GL_TRIANGLE_STRIP;
    case EPT_TRIANGLE_FAN:
        return GL_TRIANGLE_FAN;
    case EPT_TRIANGLES:
        return GL_TRIANGLES;
    case EPT_QUAD_STRIP:
        return GL_QUAD_STRIP;
    case EPT_QUADS:
        return GL_QUADS;
    case EPT_POLYGON:
        return GL_POLYGON;
    case EPT_POINT_SPRITES:
        return GL_POINT_SPRITE_ARB;
    }
    return GL_TRIANGLES;
}

//! creates a matrix in supplied GLfloat array to pass to OpenGL
inline void VideoManager_C::create_gl_matrix(GLfloat gl_matrix[16], const Myth::Math::Matrix4& m)
{
    memcpy(gl_matrix, m.pointer(), 16 * sizeof(float));
}

//! creates a opengltexturematrix from a D3D style texture matrix
inline void VideoManager_C::create_gl_texture_matrix(GLfloat* o, const Myth::Math::Matrix4& m)
{
    o[0] = m[0];
    o[1] = m[1];
    o[2] = 0.f;
    o[3] = 0.f;

    o[4] = m[4];
    o[5] = m[5];
    o[6] = 0.f;
    o[7] = 0.f;

    o[8] = 0.f;
    o[9] = 0.f;
    o[10] = 1.f;
    o[11] = 0.f;

    o[12] = m[8];
    o[13] = m[9];
    o[14] = 0.f;
    o[15] = 1.f;
}

// prints renderer version
void VideoManager_C::print_version()
{
    std::string namePrint = "Using renderer: ";
    namePrint += mName;
    mLogManager->log(namePrint.c_str(), ELL_INFORMATION);
}

//! sets the needed renderstates
void VideoManager_C::set_render_states_3d_mode()
{
    if(mCurrentRenderMode != ERM_3D)
    {
        // Reset Texture Stages
        glDisable(GL_BLEND);
        glDisable(GL_ALPHA_TEST);
        glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_COLOR);

        // switch back the matrices
        glMatrixMode(GL_MODELVIEW);
        glLoadMatrixf((mMatrices[ETS_VIEW] * mMatrices[ETS_WORLD]).pointer());

        GLfloat glmat[16];
        create_gl_matrix(glmat, mMatrices[ETS_PROJECTION]);
        glmat[12] *= -1.0f;
        glMatrixMode(GL_PROJECTION);
        glLoadMatrixf(glmat);

        mResetRenderStates = true;
    }

    if(mResetRenderStates || mLastMaterial != mMaterial)
    {
        // unset old material

        if(mLastMaterial.mMaterialType != mMaterial.mMaterialType &&
                static_cast<uint32_t>(mLastMaterial.mMaterialType) < mMaterialRenderers.size())
            mMaterialRenderers[mLastMaterial.mMaterialType].mRenderer->on_unset_material();

        // set new material.
        if(static_cast<uint32_t>(mMaterial.mMaterialType) < mMaterialRenderers.size())
            mMaterialRenderers[mMaterial.mMaterialType].mRenderer->on_set_material(
                mMaterial, mLastMaterial, mResetRenderStates, this);

        mLastMaterial = mMaterial;
        mResetRenderStates = false;
    }

    if(static_cast<uint32_t>(mMaterial.mMaterialType) < mMaterialRenderers.size())
        mMaterialRenderers[mMaterial.mMaterialType].mRenderer->on_render(this, EVT_STANDARD);

    mCurrentRenderMode = ERM_3D;
}


//! sets the needed renderstates
void VideoManager_C::set_render_states_2d_mode(bool alpha, bool texture, bool alphaChannel)
{
    if(mCurrentRenderMode != ERM_2D || mTransformation3DChanged)
    {
        // unset last 3d material
        if(mCurrentRenderMode == ERM_3D)
        {
            if(static_cast<uint32_t>(mLastMaterial.mMaterialType) < mMaterialRenderers.size())
                mMaterialRenderers[mLastMaterial.mMaterialType].mRenderer->on_unset_material();
        }
        if(mTransformation3DChanged)
        {
            glMatrixMode(GL_PROJECTION);

            const Myth::Math::Vector2u renderTargetSize = get_current_render_target_size();
            Myth::Math::Matrix4 m;
            m.buildProjectionMatrixOrthoLH(float(renderTargetSize.X), float(-(int32_t)(renderTargetSize.Y)), -1.0, 1.0);
            m.setTranslation(Myth::Math::Vector3f(-1,1,0));
            glLoadMatrixf(m.pointer());

            glMatrixMode(GL_MODELVIEW);
            glLoadIdentity();
            glTranslatef(0.375, 0.375, 0.0);

            // Make sure we set first texture matrix
            if(mMultiTextureExtension)
                extGlActiveTexture(GL_TEXTURE0_ARB);

            mTransformation3DChanged = false;
        }
        if(!mOverrideMaterial2DEnabled)
        {
            set_basic_render_states(mInitMaterial2D, mLastMaterial, true);
            mLastMaterial = mInitMaterial2D;
            glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
        }
    }
    if(mOverrideMaterial2DEnabled)
    {
        mOverrideMaterial2D.mLighting=false;
        mOverrideMaterial2D.mZBuffer=ECF_NEVER;
        mOverrideMaterial2D.mZWriteEnable=false;
        set_basic_render_states(mOverrideMaterial2D, mLastMaterial, false);
        mLastMaterial = mOverrideMaterial2D;
    }

    if(alphaChannel || alpha)
    {
        glEnable(GL_BLEND);
        glEnable(GL_ALPHA_TEST);
        glAlphaFunc(GL_GREATER, 0.f);
    }
    else
    {
        glDisable(GL_BLEND);
        glDisable(GL_ALPHA_TEST);
    }

    if(texture)
    {
        if(!mOverrideMaterial2DEnabled)
        {
            glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
            glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
            glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
            glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
        }
        set_transform(ETS_TEXTURE_0, Myth::Math::Matrix4());

        if(alphaChannel)
        {
            // if alpha and alpha texture just modulate, otherwise use only the alpha channel
            if(alpha)
            {
                glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
            }
            else
            {
                if(mFeatureAvailable[LYNX_ARB_texture_env_combine])
                {
                    glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE_ARB);
                    glTexEnvf(GL_TEXTURE_ENV, GL_COMBINE_ALPHA_ARB, GL_REPLACE);
                    glTexEnvf(GL_TEXTURE_ENV, GL_SOURCE0_ALPHA_ARB, GL_TEXTURE);
                    // rgb always modulates
                    glTexEnvf(GL_TEXTURE_ENV, GL_COMBINE_RGB_ARB, GL_MODULATE);
                    glTexEnvf(GL_TEXTURE_ENV, GL_SOURCE0_RGB_ARB, GL_TEXTURE);
                    glTexEnvf(GL_TEXTURE_ENV, GL_SOURCE1_RGB_ARB, GL_PRIMARY_COLOR_ARB);
                }
                else
                    glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
            }
        }
        else
        {
            if(alpha)
            {
                if(mFeatureAvailable[LYNX_ARB_texture_env_combine])
                {
                    glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE_ARB);
                    glTexEnvf(GL_TEXTURE_ENV, GL_COMBINE_ALPHA_ARB, GL_REPLACE);
                    glTexEnvf(GL_TEXTURE_ENV, GL_SOURCE0_ALPHA_ARB, GL_PRIMARY_COLOR_ARB);
                    // rgb always modulates
                    glTexEnvf(GL_TEXTURE_ENV, GL_COMBINE_RGB_ARB, GL_MODULATE);
                    glTexEnvf(GL_TEXTURE_ENV, GL_SOURCE0_RGB_ARB, GL_TEXTURE);
                    glTexEnvf(GL_TEXTURE_ENV, GL_SOURCE1_RGB_ARB, GL_PRIMARY_COLOR_ARB);
                }
                else
                    glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
            }
            else
            {
                glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
            }
        }
    }

    mCurrentRenderMode = ERM_2D;
}

} // namespace Lynx
